python - 属性与描述符与 __getattribute__ 的用例

标签 python properties descriptor getattribute

问题是指哪个更适合用于哪个用例,而不是技术背景。

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

我正在寻找这样的答案:

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

示例

用例是可能无法在 __init__ 方法中设置的属性,例如,因为该对象尚未出现在数据库中,但稍后会出现。每次访问属性时,都应尝试设置并返回。

作为在 Python shell 中使用复制和粘贴的示例,有一个类只想在第二次被要求时显示其属性。那么,哪一种是最好的方法,或者在不同的情况下,其中一种更可取?以下是实现它的三种方法:

具有属性::

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

最佳答案

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

  • 不要使用 property如果普通属性会起作用。
  • 如果 property 不写你自己的描述符将工作。
  • 不要使用 __getattr__如果 property将工作。
  • 不要使用 __getattribute__如果__getattr__将工作。

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

您使用 property当您有一个或一小组属性时,您想要 Hook 其获取/设置。也就是说,你想要像 obj.prop 这样的东西和 obj.prop = 2 secret 调用您编写的函数来自定义发生的情况。

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

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

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

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

关于python - 属性与描述符与 __getattribute__ 的用例,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22616559/

相关文章:

python - 了解 Python 描述符

python - Pandas :如何检查数据框列中的任何列表是否存在于另一个数据框的范围内?

python - 选择汉明距离为零的读数

visual-studio-2010 - VS 2010属性页无法显示-已与其底层RCW分离的COM对象无法使用

python - 我应该在 python 的类中使用我自己的属性吗?

c# - 如何在 C# 中应用重命名方法来更改带有 get/set 的名称?

python - 对 python 类同时使用 __setattr__ 和描述符

python - 在 Python 描述符中创建动态文档字符串

python - SQLITE3 不创建数据库

python - 在带有日期的多列上具有多个条件的子集 pandas 数据框