setattr 和赋值不等同于私有属性
setattr and assignment are not equivalent for private attributes
使用 CPython 3.4.3 (GCC 5.3.1 20160406 (Red Hat 5.3.1-6)),我在私有属性上使用 setattr
和 getattr
时遇到了一个非常奇怪的行为:
class Short:
def __init__(self):
setattr(self, '__myatt', None)
self.__myatt = 42
assert getattr(self, '__myatt') is None
Short()
尽管在断言语句之前做作,最后一段代码从未引发任何 AssertionError
。
但是,使用 myattr
而不是 __myatt
的相同代码会正确引发异常:
class Short:
def __init__(self):
setattr(self, 'myatt', None)
self.myatt = 42
assert getattr(self, 'myatt') is 42
Short()
这是两种情况(私有与 public)的比较,带有注释和断言,表明 3 种访问方式中的 2 种未 returns 预期值:
class Private:
def __init__(self):
print('############### PRIVATE ###############')
print('Assign __fa with None using setattr.')
setattr(self, '__fa', None)
print("Set __fa to 42 using regular affectation.")
new_value = 42
self.__fa = new_value
print("\nPrint the new values. Expected to be all 42:")
print("new value:", new_value)
print("self.__fa:", self.__fa)
print("getattr(self, '__fa'):", getattr(self, '__fa'))
print("self.__dict__['__fa']:", self.__dict__['__fa'])
assert self.__dict__['__fa'] is None # this is unexpected
assert getattr(self, '__fa') is None # this is unexpected
print("\nNow modify __fa using setattr")
# Maintenant, on utilise la notation «équivalente» (d'après la doc)
setattr(self, '__fa', new_value)
print("\nPrint the new values. Expected to be all 42:")
# et tout va bien !
# WTF !
print("new value:", new_value)
print("self.__fa:", self.__fa)
print("getattr(self, '__fa'):", getattr(self, '__fa'))
print("self.__dict__['__fa']:", self.__dict__['__fa'])
assert self.__fa is not None
class Public:
def __init__(self):
print('\n############### PUBLIC ###############')
print('Assign fa with None using setattr.')
setattr(self, 'fa', None)
print("Set fa to 42 using regular affectation.")
new_value = 42
self.fa = new_value
print("\nPrint the new values. Expected to be all 42:")
print("new value:", new_value)
print("self.fa:", self.fa)
print("getattr(self, 'fa'):", getattr(self, 'fa'))
print("self.__dict__['fa']:", self.__dict__['fa'])
assert self.__dict__['fa'] is not None # this is expected
assert getattr(self, 'fa') is not None # this is expected
Private()
Public()
这种行为是预期的吗?为什么以及它的起源是什么?
这是意料之中的。在编译时执行的名称修改不会影响字符串文字,因此您应该在 getattr
、setattr
和 hasattr
:
中明确提供修改后的名称
class Short:
def __init__(self):
self.__myatt = 42
# print(getattr(self, '__myatt')) # Fails with a NameError
print(getattr(self, '_{0.__qualname__}__myatt'.format(type(self)))) # Succeeds
现在打印出 42
。同样,要设置 setattr
,要检查 hasattr
,您需要提供经过处理的名称。
这是在相关 bug report 中提出的,因为 hasattr
是的,它被明确声明为预期行为。
使用 CPython 3.4.3 (GCC 5.3.1 20160406 (Red Hat 5.3.1-6)),我在私有属性上使用 setattr
和 getattr
时遇到了一个非常奇怪的行为:
class Short:
def __init__(self):
setattr(self, '__myatt', None)
self.__myatt = 42
assert getattr(self, '__myatt') is None
Short()
尽管在断言语句之前做作,最后一段代码从未引发任何 AssertionError
。
但是,使用 myattr
而不是 __myatt
的相同代码会正确引发异常:
class Short:
def __init__(self):
setattr(self, 'myatt', None)
self.myatt = 42
assert getattr(self, 'myatt') is 42
Short()
这是两种情况(私有与 public)的比较,带有注释和断言,表明 3 种访问方式中的 2 种未 returns 预期值:
class Private:
def __init__(self):
print('############### PRIVATE ###############')
print('Assign __fa with None using setattr.')
setattr(self, '__fa', None)
print("Set __fa to 42 using regular affectation.")
new_value = 42
self.__fa = new_value
print("\nPrint the new values. Expected to be all 42:")
print("new value:", new_value)
print("self.__fa:", self.__fa)
print("getattr(self, '__fa'):", getattr(self, '__fa'))
print("self.__dict__['__fa']:", self.__dict__['__fa'])
assert self.__dict__['__fa'] is None # this is unexpected
assert getattr(self, '__fa') is None # this is unexpected
print("\nNow modify __fa using setattr")
# Maintenant, on utilise la notation «équivalente» (d'après la doc)
setattr(self, '__fa', new_value)
print("\nPrint the new values. Expected to be all 42:")
# et tout va bien !
# WTF !
print("new value:", new_value)
print("self.__fa:", self.__fa)
print("getattr(self, '__fa'):", getattr(self, '__fa'))
print("self.__dict__['__fa']:", self.__dict__['__fa'])
assert self.__fa is not None
class Public:
def __init__(self):
print('\n############### PUBLIC ###############')
print('Assign fa with None using setattr.')
setattr(self, 'fa', None)
print("Set fa to 42 using regular affectation.")
new_value = 42
self.fa = new_value
print("\nPrint the new values. Expected to be all 42:")
print("new value:", new_value)
print("self.fa:", self.fa)
print("getattr(self, 'fa'):", getattr(self, 'fa'))
print("self.__dict__['fa']:", self.__dict__['fa'])
assert self.__dict__['fa'] is not None # this is expected
assert getattr(self, 'fa') is not None # this is expected
Private()
Public()
这种行为是预期的吗?为什么以及它的起源是什么?
这是意料之中的。在编译时执行的名称修改不会影响字符串文字,因此您应该在 getattr
、setattr
和 hasattr
:
class Short:
def __init__(self):
self.__myatt = 42
# print(getattr(self, '__myatt')) # Fails with a NameError
print(getattr(self, '_{0.__qualname__}__myatt'.format(type(self)))) # Succeeds
现在打印出 42
。同样,要设置 setattr
,要检查 hasattr
,您需要提供经过处理的名称。
这是在相关 bug report 中提出的,因为 hasattr
是的,它被明确声明为预期行为。