Mad*_*ist 8 python class python-3.x
我试图__dict__直接使用类似的东西修改类中的值X.__dict__['x'] += 1.这样的修改是不可能的,因为类__dict__实际上是一个mappingproxy不允许直接修改值的对象.尝试直接修改或等效的原因是我试图隐藏使用相同名称在元类上定义的属性后面的class属性.这是一个例子:
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
__class__.__dict__['x'] += 1
Run Code Online (Sandbox Code Playgroud)
此示例显示了为每个实例创建自动递增ID的方案Class.该行__class__.__dict__['x'] += 1不能被替换为setattr(__class__, 'x', __class__.x + 1)因为x是property没有setter的Meta.它只是改变TypeError从mappingproxy到AttributeError从property.
我试过搞乱__prepare__,但这没有效果.实现中type已经dict为命名空间返回了一个mutable .不可变mappingproxy似乎被设置type.__new__,我不知道如何避免.
我也试图重新__dict__引用一个可变版本的整个引用,但也失败了:https://ideone.com/w3HqNf,暗示可能mappingproxy没有创建type.__new__.
如何dict直接修改类值,即使被元类属性遮蔽?虽然它可能实际上是不可能的,但setattr能够以某种方式完成,所以我希望有一个解决方案.
我的主要要求是拥有一个看似只读的类属性,并且不会在任何地方使用其他名称.我并不是完全依赖于使用property具有同名类dict条目的元类的想法,但这通常是我在常规实例中隐藏只读值的方式.
编辑
我终于找到了类__dict__变为不可变的地方.它在数据模型参考的"创建类对象"部分的最后一段中进行了描述:
创建新类时
type.__new__,将作为namespace参数提供的对象复制到新的有序映射,并丢弃原始对象.新副本包装在只读代理中,该代理成为__dict__类对象的属性.
可能是最好的方法:选择另一个名字.调用属性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
Run Code Online (Sandbox Code Playgroud)
这样您就不必修改类dict中的实际条目.
超级hacky方式可能会彻底破坏你的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
Run Code Online (Sandbox Code Playgroud)
这绕过type.__setattr__了维护内部不变量的关键工作,特别是在CPython的类型属性缓存之类的东西中.这是一个可怕的想法,我只是提到它,所以我可以在这里提出这个警告,因为如果其他人提出它,他们可能不知道搞乱根本的dict是合法的危险.
这样做很容易让悬空引用结束,而且我已经多次尝试使用segfaulted Python进行实验.这是一个在Ideone上崩溃的简单案例:
import gc
class Foo(object):
x = []
Foo().x
gc.get_referents(Foo.__dict__)[0]['x'] = []
print(Foo().x)
Run Code Online (Sandbox Code Playgroud)
输出:
*** 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]
Run Code Online (Sandbox Code Playgroud)
而这里的情况与错误的结果并没有嘈杂的错误信息,提醒你一个事实,即出了问题:
import gc
class Foo(object):
x = 'foo'
print(Foo().x)
gc.get_referents(Foo.__dict__)[0]['x'] = 'bar'
print(Foo().x)
Run Code Online (Sandbox Code Playgroud)
输出:
foo
foo
Run Code Online (Sandbox Code Playgroud)
我绝对不保证任何安全的方式使用它,即使事情碰巧在一个Python版本上运行,它们可能无法在未来的版本上工作.摆弄它可能很有趣,但它不是实际使用的东西.说真的,不要这样做.您是否想向您的老板解释您的网站发生故障或者您需要撤回已发布的数据分析,因为您采用了这个糟糕的想法并使用它?
| 归档时间: |
|
| 查看次数: |
474 次 |
| 最近记录: |