18

问题是指在哪个用例中最好使用哪一个,而不是技术背景。

在 python 中,您可以通过属性描述符魔术方法来控制属性的访问。在哪个用例中哪一个是最 Pythonic 的?它们似乎都具有相同的效果(请参见下面的示例)。

我正在寻找类似的答案:

  • 属性:应该在……的情况下使用。</li>
  • Descriptor:在……的情况下,应该使用它而不是属性。
  • 魔术方法:仅在……时使用。

例子

用例是可能无法在__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)

__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

描述符::

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()

每一个都有这种行为::

book = ContactBook()
print(book.first_person)
# >>None
print(book.first_person)
# >>Mr. First
4

2 回答 2

20

基本上,尽可能使用最简单的一种。粗略地说,复杂性/繁重性的顺序是:常规属性、、、property/__getattr__描述__getattribute__符。(__getattribute__和自定义描述符都是您可能不需要经常做的事情。)这导致了一些简单的经验法则:

  • property如果正常属性可以工作,请不要使用。
  • 如果 a 可行,请不要编写自己的描述符property
  • __getattr__如果 aproperty可以工作,请不要使用。
  • 不要使用__getattribute__if__getattr__将工作。

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

property当您有一个或一小组要挂钩的获取/设置属性时,您可以使用。也就是说,您想要obj.propobj.prop = 2秘密调用您编写的函数来自定义发生的事情。

__getattr__当您想要对如此多的属性执行此操作时,您将使用这些属性,您实际上并不想单独定义它们,而是希望将整个属性访问过程作为一个整体进行自定义。换句话说,不是挂钩到obj.prop1, 和obj.prop2等等,你有很多你希望能够挂钩到obj.<anything>,并在一般情况下处理它。

但是,__getattr__仍然不会让您覆盖真正存在的属性所发生的事情,它只是让您对任何会引发 AttributeError 的属性的使用进行全面处理。Using__getattribute__可以让你处理所有事情,甚至是正常的属性,这些属性本来可以正常工作而不会弄乱__getattribute__。正因为如此, using__getattribute__有可能破坏相当基本的行为,所以你应该只在你考虑 using__getattr__并且还不够的情况下才使用它。它还可以产生显着的性能影响。例如,您可能需要使用__getattribute__如果您要包装一个定义某些属性的类,并且您希望能够以自定义方式包装这些属性,以便它们在某些情况下照常工作,但在其他情况下获得自定义行为。

最后,我想说编写自己的描述符是一项相当高级的任务。 property是一个描述符,对于大约 95% 的情况,它是您唯一需要的。这里给出了一个很好的简单示例,说明为什么要编写自己的描述符:基本上,如果您必须编写几个property具有相似行为的 s,则可以这样做;描述符可让您分解常见行为以避免代码重复。例如,自定义描述符用于驱动 Django 和 SQLAlchemy 等系统。如果您发现自己在编写复杂程度的内容,则可能需要编写自定义描述符。

在您的示例中,property将是最佳选择。如果你在if name == 'somespecificname'里面做,通常(并不总是)是一个危险信号__getattribute__。如果您只需要专门处理一个特定的名称,您可能可以做到这一点,而不会屈服于__getattribute__. __get__同样,如果您为它编写的所有内容都是您可以在属性的 getter 方法中编写的,那么编写您自己的描述符是没有意义的。

于 2014-03-24T18:07:51.103 回答
2

__getattribute__是使property(和其他描述符)首先工作的钩子,并且在对象上的所有属性访问中被调用。当属性甚至自定义描述符不足以满足您的需求时,可以将其视为较低级别的 API。当没有通过其他方式找到属性时__getattr__调用,作为后备。__getattribute__

用于property具有固定名称的动态属性,__getattr__用于更动态的属性(例如,一系列以算法方式映射到值的属性)。

当您需要将任意对象绑定到实例时使用描述符。例如,当您需要用更高级的东西替换方法对象时;最近的一个例子是基于类的装饰器包装方法,需要支持方法对象上的其他属性和方法。通常,当您仍在考虑标量属性时,您不需要描述符。

于 2014-03-24T17:38:00.287 回答