gpt4 book ai didi

python - 什么是实现描述符的正确方法?

转载 作者:太空宇宙 更新时间:2023-11-04 00:29:07 25 4
gpt4 key购买 nike

考虑这段代码:在 python 3.6 上运行

Bar 给描述符实例赋值

Bat 将值分配给包含的类实例。

我见过的代码示例(并且习惯了我无尽的挫败感)使用 Bar 示例。比如这个site

来自 python docs

从使用 Bar 示例的输出可以看出,一个类的两个实例不能使用相同的描述符。

还是我遗漏了什么?

class DescriptorA(object):
value = None
def __get__(self, instance, owner):
return self.value

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

class DescriptorB(object):
_value = None
def __get__(self, instance, owner):
return instance._value

def __set__(self, instance, value):
instance._value = value


class Bar(object):
foo = DescriptorA()
def __init__(self, foo):
self.foo = foo

class Bat(object):
foo = DescriptorB()
def __init__(self, foo):
self.foo = foo


print('BAR')
a = Bar(1)
print('a', a.foo)

b = Bar(2)
print('b', b.foo)
print('Checking a')
print('a', a.foo)

print('BAT')
c = Bat(3)
print('c', c.foo)

d = Bat(4)
print('d', d.foo)
print('Checking c')
print('c', c.foo)

输出

BAR
a 1
b 2
Checking a
a 2
BAT
c 3
d 4
Checking c
c 3

更新

只是想添加这个。为了回应好的答案。当不使用描述符,但仍然使用类属性时。我们得到不同的行为。这就是我错误地使用 DescriptorA 的原因。

class Bar(object):
foo = None
def __init__(self, foo):
self.foo = foo

class Bat(object):
foo = None
def __init__(self, foo):
self.foo = foo


print('BAR')
a = Bar(1)
print('a', a.foo)

b = Bar(2)
print('b', b.foo)
print('Checking a')
print('a', a.foo)

print('BAT')
c = Bat(3)
print('c', c.foo)

d = Bat(4)
print('d', d.foo)
print('Checking c')
print('c', c.foo)

BAR
a 1
b 2
Checking a
a 1
BAT
c 3
d 4
Checking c
c 3

最佳答案

这实际上取决于您的用例如何存储变量。

我们有 4 个对象,每个对象都有自己的一组变量:

  • 描述符类
  • 描述符的实例
  • 普通类
  • 普通类实例

通常,描述符实例 存储在“普通类”中,因为当您将描述符存储在实例中时,不会调用描述符协议(protocol)。您也可以“转到元”并在元类上使用描述符或在描述符内使用描述符,但为了保持简短和理智,让我们忽略这些(这并不难,但可能有点太宽泛了)。

所以对于您的DescriptorA,您存储:

  • value = None 在描述符类中
  • value = ? 在描述符实例中(至少在调用 __set__ 至少一次之后
  • foo = 普通类中的描述符实例
  • 类实例中没有任何内容

DescriptorB 的情况下,您存储:

  • _value = None 在描述符类中
  • 描述符实例中没有任何内容
  • foo = 普通类中的描述符实例
  • _value = ? 在普通类的实例中

看出区别了吗?在第一种情况下,普通类的不同实例访问相同的描述符实例,因此所有内容都是共享的。在第二种情况下,您将所有内容都存储在类的实例中,而在描述符实例中没有任何内容,因此不会共享任何内容。

请注意,您的 DescriptorB 看起来很奇怪,为什么在描述符类中存储 _value = None,而您从不使用它?请记住,您访问的是普通类实例的 _value,而不是 __get__ 中的描述符实例的 _value!

正如我之前所说,选择哪种方法取决于您的用例。通常您希望拥有一些共享属性和一些实例属性。但是您也可以在描述符的所有实例之间共享属性,并且假设您还可以在 __get__ 中访问普通类实例的类型并在 type(instance) 中使用 code>__set__ 你也可以修改普通类的类属性。

例如 Python 文档中的示例:

class RevealAccess(object):
"""A data descriptor that sets and returns values
normally and prints a message logging their access.
"""

def __init__(self, initval=None, name='var'):
self.val = initval
self.name = name

def __get__(self, obj, objtype):
print('Retrieving', self.name)
return self.val

def __set__(self, obj, val):
print('Updating', self.name)
self.val = val

>>> class MyClass(object):
... x = RevealAccess(10, 'var "x"')
... y = 5
...

他们故意为类变量 创建了一个描述符。在那种情况下,就没有“实例”,它是否被共享并不重要,因为默认情况下类变量将被实例共享。这意味着即使您在一个实例上设置变量,它也会为所有其他实例发生变化。

因此,如果您不想共享描述符实例变量,则不应真正使用它。但是,您应该将它们用于应该共享的所有内容(例如属性的名称等)。

可能同样有趣的可能是 Python 查找属性的“方式”。我通常从 this blog 中找到这张图片非常有用:

enter image description here

关于python - 什么是实现描述符的正确方法?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46445773/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com