免费视频淫片aa毛片_日韩高清在线亚洲专区vr_日韩大片免费观看视频播放_亚洲欧美国产精品完整版

打開APP
userphoto
未登錄

開通VIP,暢享免費(fèi)電子書等14項(xiàng)超值服

開通VIP
深入描述符

描述符是一種在多個(gè)屬性上重復(fù)利用同一個(gè)存取邏輯的方式,他能'劫持'那些本對(duì)于self.__dict__的操作。描述符通常是一種包含__get__、__set__、__delete__三種方法中至少一種的類,給人的感覺是「把一個(gè)類的操作托付與另外一個(gè)類」。靜態(tài)方法、類方法、property都是構(gòu)建描述符的類。

我們先看一個(gè)簡(jiǎn)單的描述符的例子(基于我之前的分享的Python高級(jí)編程改編,這個(gè)PPT建議大家去看看):

  1. class MyDescriptor(object):

  2.     _value = ''

  3.     def __get__(self, instance, klass):

  4.         return self._value

  5.     def __set__(self, instance, value):

  6.         self._value = value.swapcase()

  7. class Swap(object):

  8.     swap = MyDescriptor()

注意MyDescriptor要用新式類。調(diào)用一下:

  1. In [1]: from descriptor_example import Swap

  2. In [2]: instance = Swap()

  3. In [3]: instance.swap  # 沒有報(bào)AttributeError錯(cuò)誤,因?yàn)閷?duì)swap的屬性訪問被描述符類重載了

  4. Out[3]: ''

  5. In [4]: instance.swap = 'make it swap'  # 使用__set__重新設(shè)置_value

  6. In [5]: instance.swap

  7. Out[5]: 'MAKE IT SWAP'

  8. In [6]: instance.__dict__  # 沒有用到__dict__:被劫持了

  9. Out[6]: {}

這就是描述符的威力。我們熟知的staticmethod、classmethod如果你不理解,那么看一下用Python實(shí)現(xiàn)的效果可能會(huì)更清楚了:

  1. >>> class myStaticMethod(object):

  2. ...     def __init__(self, method):

  3. ...         self.staticmethod = method

  4. ...     def __get__(self, object, type=None):

  5. ...         return self.staticmethod

  6. ...

  7. >>> class myClassMethod(object):

  8. ...     def __init__(self, method):

  9. ...         self.classmethod = method

  10. ...     def __get__(self, object, klass=None):

  11. ...         if klass is None:

  12. ...             klass = type(object)

  13. ...         def newfunc(*args):

  14. ...             return self.classmethod(klass, *args)

  15. ...         return newfunc

在實(shí)際的生產(chǎn)項(xiàng)目中,描述符有什么用處呢?首先看MongoEngine中的Field的用法:

  1. from mongoengine import *                      

  2. class Metadata(EmbeddedDocument):                  

  3.    tags = ListField(StringField())

  4.    revisions = ListField(IntField())

  5. class WikiPage(Document):                          

  6.    title = StringField(required=True)              

  7.    text = StringField()                            

  8.    metadata = EmbeddedDocumentField(Metadata)

有非常多的Field類型,其實(shí)它們的基類就是一個(gè)描述符,我簡(jiǎn)化下,大家看看實(shí)現(xiàn)的原理:

  1. class BaseField(object):

  2.    name = None

  3.    def __init__(self, **kwargs):

  4.        self.__dict__.update(kwargs)

  5.        ...

  6.    def __get__(self, instance, owner):

  7.        return instance._data.get(self.name)

  8.    def __set__(self, instance, value):

  9.        ...

  10.        instance._data[self.name] = value

很多項(xiàng)目的源代碼看起來很復(fù)雜,在抽絲剝繭之后,其實(shí)原理非常簡(jiǎn)單,復(fù)雜的是業(yè)務(wù)邏輯。

接著我們?cè)倏碏lask的依賴Werkzeug中的cached_property:

  1. class _Missing(object):

  2.    def __repr__(self):

  3.        return 'no value'

  4.    def __reduce__(self):

  5.        return '_missing'

  6. _missing = _Missing()

  7. class cached_property(property):

  8.    def __init__(self, func, name=None, doc=None):

  9.        self.__name__ = name or func.__name__

  10.        self.__module__ = func.__module__

  11.        self.__doc__ = doc or func.__doc__

  12.        self.func = func

  13.    def __set__(self, obj, value):

  14.        obj.__dict__[self.__name__] = value

  15.    def __get__(self, obj, type=None):

  16.        if obj is None:

  17.            return self

  18.        value = obj.__dict__.get(self.__name__, _missing)

  19.        if value is _missing:

  20.            value = self.func(obj)

  21.            obj.__dict__[self.__name__] = value

  22.        return value

其實(shí)看類的名字就知道這是緩存屬性的,看不懂沒關(guān)系,用一下:

  1. class Foo(object):

  2.    @cached_property

  3.    def foo(self):

  4.        print 'Call me!'

  5.        return 42

調(diào)用下:

  1. In [1]: from cached_property import Foo

  2.   ...: foo = Foo()

  3.   ...:

  4. In [2]: foo.bar

  5. Call me!

  6. Out[2]: 42

  7. In [3]: foo.bar

  8. Out[3]: 42

可以看到在從第二次調(diào)用bar方法開始,其實(shí)用的是緩存的結(jié)果,并沒有真的去執(zhí)行。

說了這么多描述符的用法。我們寫一個(gè)做字段驗(yàn)證的描述符:

  1. class Quantity(object):

  2.    def __init__(self, name):

  3.        self.name = name

  4.    def __set__(self, instance, value):

  5.        if value > 0:

  6.            instance.__dict__[self.name] = value

  7.        else:

  8.            raise ValueError('value must be > 0')

  9. class Rectangle(object):

  10.    height = Quantity('height')

  11.    width = Quantity('width')

  12.    def __init__(self, height, width):

  13.        self.height = height

  14.        self.width = width

  15.    @property

  16.    def area(self):

  17.        return self.height * self.width

我們?cè)囈辉嚕?/p>

  1. In [1]: from rectangle import Rectangle

  2. In [2]: r = Rectangle(10, 20)

  3. In [3]: r.area

  4. Out[3]: 200

  5. In [4]: r = Rectangle(-1, 20)

  6. ---------------------------------------------------------------------------

  7. ValueError                                Traceback (most recent call last)

  8. ipython-input-5-5a7fc56e8a> in module>()

  9. ----> 1 r = Rectangle(-1, 20)

  10. /Users/dongweiming/mp/2017-03-23/rectangle.py in __init__(self, height, width)

  11.     15

  12.     16     def __init__(self, height, width):

  13. ---> 17         self.height = height

  14.     18         self.width = width

  15.     19

  16. /Users/dongweiming/mp/2017-03-23/rectangle.py in __set__(self, instance, value)

  17.      7             instance.__dict__[self.name] = value

  18.      8         else:

  19. ----> 9             raise ValueError('value must be > 0')

  20.     10

  21.     11

  22. ValueError: value must be > 0

看到了吧,我們?cè)诿枋龇念惱锩鎸?duì)傳值進(jìn)行了驗(yàn)證。ORM就是這么玩的!

但是上面的這個(gè)實(shí)現(xiàn)有個(gè)缺點(diǎn),就是不太自動(dòng)化,你看 height =Quantity('height'),這得讓屬性和Quantity的name都叫做height,那么可不可以不用指定name呢?當(dāng)然可以,不過實(shí)現(xiàn)的要復(fù)雜很多:

  1. class Quantity(object):

  2.    __counter = 0

  3.    def __init__(self):

  4.        cls = self.__class__

  5.        prefix = cls.__name__

  6.        index = cls.__counter

  7.        self.name = '_{}#{}'.format(prefix, index)

  8.        cls.__counter += 1

  9.    def __get__(self, instance, owner):

  10.        if instance is None:

  11.            return self

  12.        return getattr(instance, self.name)

  13.    ...

  14. class Rectangle(object):

  15.    height = Quantity()

  16.    width = Quantity()

  17.    ...

Quantity的name相當(dāng)于類名+計(jì)時(shí)器,這個(gè)計(jì)時(shí)器每調(diào)用一次就疊加1,用此區(qū)分。有一點(diǎn)值得提一提,在__get__中的:

  1. if instance is None:

  2.    return self

在很多地方可見,比如之前提到的MongoEngine中的BaseField。這是由于直接調(diào)用Rectangle.height這樣的屬性時(shí)候會(huì)報(bào)AttributeError, 因?yàn)槊枋龇菍?shí)例上的屬性。

PS:這個(gè)靈感來自《Fluent Python》,書中還有一個(gè)我認(rèn)為設(shè)計(jì)非常好的例子。就是當(dāng)要驗(yàn)證的內(nèi)容種類很多的時(shí)候,如何更好地?cái)U(kuò)展的問題?,F(xiàn)在假設(shè)我們除了驗(yàn)證傳入的值要大于0,還得驗(yàn)證不能為空和必須是數(shù)字(當(dāng)然三種驗(yàn)證在一個(gè)方法中驗(yàn)證也是可以接受的,我這里就是個(gè)演示),我們先寫一個(gè)abc的基類:

  1. class Validated(abc.ABC):

  2.    __counter = 0

  3.    def __init__(self):

  4.        cls = self.__class__

  5.        prefix = cls.__name__

  6.        index = cls.__counter

  7.        self.name = '_{}#{}'.format(prefix, index)

  8.        cls.__counter += 1

  9.    def __get__(self, instance, owner):

  10.        if instance is None:

  11.            return self

  12.        else:

  13.            return getattr(instance, self.name)

  14.    def __set__(self, instance, value):

  15.        value = self.validate(instance, value)

  16.        setattr(instance, self.name, value)

  17.    @abc.abstractmethod

  18.    def validate(self, instance, value):

  19.        '''return validated value or raise ValueError'''

現(xiàn)在新加一個(gè)檢查類型,新增一個(gè)繼承了Validated的、包含檢查的validate方法的類就可以了:

  1. class Quantity(Validated):

  2.    def validate(self, instance, value):

  3.        if value <> 0:

  4.            raise ValueError('value must be > 0')

  5.        return value

  6. class NonBlank(Validated):

  7.    def validate(self, instance, value):

  8.        value = value.strip()

  9.        if len(value) == 0:

  10.            raise ValueError('value cannot be empty or blank')

  11.        return value

前面展示的描述符都是一個(gè)類,那么可不可以用函數(shù)來實(shí)現(xiàn)呢?也是可以的:

  1. def quantity():

  2.    try:

  3.        quantity.counter += 1

  4.    except AttributeError:

  5.        quantity.counter = 0

  6.    storage_name = '_{}:{}'.format('quantity', quantity.counter)

  7.    def qty_getter(instance):

  8.        return getattr(instance, storage_name)

  9.    def qty_setter(instance, value):

  10.        if value > 0:

  11.            setattr(instance, storage_name, value)

  12.        else:

  13.            raise ValueError('value must be > 0')

  14.    return property(qty_getter, qty_setter)

本站僅提供存儲(chǔ)服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊舉報(bào)。
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
Python魔法方法漫游指南:描述符
Python 中的 SOLID 原則
11. (譯)Python魔法方法指南
python的類和對(duì)象
《源碼探秘 CPython》74. 徹底搞懂描述符
技術(shù)圖文:什么是Python的描述符?
更多類似文章 >>
生活服務(wù)
分享 收藏 導(dǎo)長(zhǎng)圖 關(guān)注 下載文章
綁定賬號(hào)成功
后續(xù)可登錄賬號(hào)暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服