属性与描述符的使用案例与__getattribute__

Iod*_*nas 14 python properties descriptor getattribute

问题是哪一个最好用于哪个用例,而不是技术背景.

在python中,您可以通过属性,描述符魔术方法来控制属性的访问.哪个用例最具pythonic?所有这些似乎都具有相同的效果(参见下面的例子).

我正在寻找一个答案:

  • 财产:应该用于......
  • 描述符:在...的情况下,应该使用它而不是属性.
  • 魔术方法:仅在......时使用.

用例将是可能无法在__init__方法中设置的属性,例如因为该对象尚未存在于数据库中,而是在稍后的时间.每次访问该属性时,都应尝试设置并返回该属性.

作为在Python shell中使用Copy&Paste的示例,有一个类只想在第二次询问它时才显示其属性.那么,哪一个是最好的方式,还是有不同的情况,其中一个更好?以下是实现它的三种方法:

随着物业 ::

class ContactBook(object):
    intents = 0

    def __init__(self):
        self.__first_person = None

    def get_first_person(self):
        ContactBook.intents += 1
        if self.__first_person is None:
            if ContactBook.intents > 1:
                value = 'Mr. First'
                self.__first_person = value
            else:
                return None
        return self.__first_person

    def set_first_person(self, value):
        self.__first_person = value

    first_person = property(get_first_person, set_first_person)
Run Code Online (Sandbox Code Playgroud)

__getattribute__::

class ContactBook(object):
    intents = 0

    def __init__(self):
        self.first_person = None

    def __getattribute__(self, name):
        if name == 'first_person' \
                and object.__getattribute__(self, name) is None:
            ContactBook.intents += 1
            if ContactBook.intents > 1:
                value = 'Mr. First'
                self.first_person = value
            else:
                value = None
        else:
            value = object.__getattribute__(self, name)
        return value
Run Code Online (Sandbox Code Playgroud)

描述符 ::

class FirstPerson(object):
    def __init__(self, value=None):
        self.value = None

    def __get__(self, instance, owner):
        if self.value is None:
            ContactBook.intents += 1
            if ContactBook.intents > 1:
                self.value = 'Mr. First'
            else:
                return None
        return self.value


class ContactBook(object):
    intents = 0
    first_person = FirstPerson()
Run Code Online (Sandbox Code Playgroud)

每一个都有这种行为::

book = ContactBook()
print(book.first_person)
# >>None
print(book.first_person)
# >>Mr. First
Run Code Online (Sandbox Code Playgroud)

Bre*_*arn 19

基本上,使用最简单的一个.大致说来,复杂性/重型岬的顺序云:常规属性property,__getattr__,__getattribute__/描述符.(__getattribute__和自定义描述符都是你可能不需要经常做的事情.)这导致了一些简单的经验法则:

  • property如果普通属性不起作用,请不要使用.
  • 如果一个property可行的话,不要编写自己的描述符.
  • __getattr__如果一个property会工作,请不要使用.
  • 不要使用__getattribute__if if __getattr__work.

更具体地说明:使用属性来自定义一个或一小组属性的处理; 用于__getattr__自定义所有属性的处理,或除了一小组之外的所有属性; 使用,__getattribute__如果你希望使用,__getattr__但它不起作用; 如果您正在做一些非常复杂的事情,请编写自己的描述符类

property如果您有一个或一小组要获取/设置的属性,则使用a .也就是说,你想要obj.prop和你obj.prop = 2秘密调用你编写的函数来定制发生的事情.

你会使用__getattr__,当你想要做的是为你实际上并不希望单独定义所有的这么多的属性,而是要自定义的全属性的访问过程作为一个整体.换句话说,而不是钩住obj.prop1,和obj.prop2等,你有这么多,你希望能够挂接到obj.<anything>,并处理一般.

但是,__getattr__仍然不会让你覆盖真正存在的属性所发生的事情,它只是让你用一个毯子处理来使用任何可能会引发AttributeError的属性.使用__getattribute__允许您挂钩处理所有内容,甚至是可以正常工作而不会搞砸的普通属性__getattribute__.因此,使用__getattribute__有可能破坏相当基本的行为,所以你应该只在你考虑使用__getattr__它时才使用它,这是不够的.它还可以产生明显的性能影响.例如,__getattribute__如果要包装定义某些属性的类,并且希望能够以自定义方式包装这些属性,则可能需要使用它们,以便它们在某些情况下照常工作,但在其他情况下获取自定义行为.

最后,我想说编写自己的描述符是一项相当高级的任务. property是一个描述符,大概95%的情况下它是你唯一需要的.你为什么会写自己的描述符一个很好的简单的例子给出了这里:基本上,你可以做,如果你本来有写一些propertys的类似的行为; 描述符允许您分解常见行为以避免代码重复.例如,自定义描述符用于驱动像Django和SQLAlchemy这样的系统.如果您发现自己在复杂程度上写了一些内容,则可能需要编写自定义描述符.

在您的示例中,property将是最佳选择.如果你在if name == 'somespecificname'里面做的话,它通常(并不总是)是一面红旗__getattribute__.如果您只需要专门处理一个特定的名称,您可以在不弯腰到达的情况下执行此操作__getattribute__.同样地,如果您为其编写的所有内容__get__都是您可以在属性的getter方法中编写的,那么编写自己的描述符是没有意义的.