也许最好的方法是:选择另一个名字。致电物业x
和字典键'_x'
,这样您就可以以正常方式访问它。
替代方法:添加另一层间接:
class Meta(type):
def __new__(cls, name, bases, attrs, **kwargs):
attrs['x'] = [0]
return super().__new__(cls, name, bases, attrs)
@property
def x(cls):
return cls.__dict__['x'][0]
class Class(metaclass=Meta):
def __init__(self):
self.id = __class__.x
__class__.__dict__['x'][0] += 1
这样你就不必修改类字典中的实际条目。
超级黑客的方式可能会彻底导致你的Python出现段错误:通过访问底层字典gc
module.
import gc
class Meta(type):
def __new__(cls, name, bases, attrs, **kwargs):
attrs['x'] = 0
return super().__new__(cls, name, bases, attrs)
@property
def x(cls):
return cls.__dict__['x']
class Class(metaclass=Meta):
def __init__(self):
self.id = __class__.x
gc.get_referents(__class__.__dict__)[0]['x'] += 1
这绕过了关键工作type.__setattr__
确实可以维护内部不变量,特别是在 CPython 的类型属性缓存等方面。这是一个糟糕的想法,我只是提到它,这样我就可以把这个警告放在这里,因为如果其他人提出这个想法,他们可能不知道弄乱底层字典是非常危险的。
这样做很容易导致悬空引用,而且我在尝试这样做时已经多次出现 Python 段错误。这是一个简单的案例在 Ideone 上崩溃:
import gc
class Foo(object):
x = []
Foo().x
gc.get_referents(Foo.__dict__)[0]['x'] = []
print(Foo().x)
Output:
*** Error in `python3': double free or corruption (fasttop): 0x000055d69f59b110 ***
======= Backtrace: =========
/lib/x86_64-linux-gnu/libc.so.6(+0x70bcb)[0x2b32d5977bcb]
/lib/x86_64-linux-gnu/libc.so.6(+0x76f96)[0x2b32d597df96]
/lib/x86_64-linux-gnu/libc.so.6(+0x7778e)[0x2b32d597e78e]
python3(+0x2011f5)[0x55d69f02d1f5]
python3(+0x6be7a)[0x55d69ee97e7a]
python3(PyCFunction_Call+0xd1)[0x55d69efec761]
python3(PyObject_Call+0x47)[0x55d69f035647]
... [it continues like that for a while]
And 这是一个案例结果错误,并且没有嘈杂的错误消息来提醒您出现问题的事实:
import gc
class Foo(object):
x = 'foo'
print(Foo().x)
gc.get_referents(Foo.__dict__)[0]['x'] = 'bar'
print(Foo().x)
Output:
foo
foo
我绝对不保证任何安全的使用方式,即使事情恰好在一个 Python 版本上运行,它们也可能无法在未来的版本上运行。摆弄它可能很有趣,但它并不是真正可以使用的东西。说真的,不要这样做。你want向你的老板解释你的网站瘫痪了,或者你发布的数据分析需要被撤回,因为你接受了这个坏主意并使用了它?