可能是最好的方法:选择另一个名称。调用属性x
和dict键'_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
这样,您不必修改类dict中的实际条目。
可能会彻底隔离您的Python的超级骇客方式:通过gc
模块访问基础dict 。
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)
输出:
*** 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]
而这里的情况与错误的结果并没有嘈杂的错误消息,提醒您的是什么地方出了错误的事实:
import gc
class Foo(object):
x = 'foo'
print(Foo().x)
gc.get_referents(Foo.__dict__)[0]['x'] = 'bar'
print(Foo().x)
输出:
foo
foo
我绝对不保证使用此方法的任何安全方法,即使在一个Python版本上发生了问题,它们也可能在将来的版本上不起作用。弄弄它可能很有趣,但是实际上并没有用。说真的,不要这样做。您是否想 向老板解释您的网站已关闭,或者因为您接受并使用了这个错误的主意而需要撤消已发布的数据分析?