如果 `__dict__` 被覆盖,则得到 true `__dict__`

问题描述 投票:0回答:1

如果

__dict__
被覆盖,是否可以获得对象的 true
__dict__
?还有比下面更简单的解决方案吗?

我遇到了这个例子,其中

__dict__
被覆盖并感到好奇。我以为 python 正在使用
__dict__
来识别对象的属性,但事实证明它可以被覆盖并且属性仍然有效。所以,原来的
__dict__
仍然在那里。

class MyClass:
    __dict__ = {}
obj = MyClass()
obj.hacky = 5
# 5, {}
print(obj.hacky, obj.__dict__)
python ctypes magic-methods
1个回答
0
投票

寻找解决方案我找到了这个。虽然它有点 hacky,但它有效 - 它交换

__class__
来访问原始
__dict__
,然后将其交换回来。它甚至可以与
__class__
__setattr__
__dict__
一起用于其他用途。

def get_true_dict(obj: object) -> dict:
    cls = type(obj)
    newcls = type('',(),{}) # one line empty class
    # __class__ is <attribute '__class__' of 'object' objects>
    object.__dict__['__class__'].__set__(obj, newcls)
    # obj.__class__ = newcls # works only if `__class__` and `__setattr__` are not "bad"
    res = obj.__dict__
    # no safety as class is fine
    obj.__class__ = cls
    return res

# normal
class X: ...
x = X()
x.a = 42
assert get_true_dict(x) is x.__dict__

# blocked setattr and __dict__ and __class__
d = {}
class X:
    __dict__ = d
    __class__ = 42
    __setattr__ = lambda *_: 1/0
x = X()
assert x.__dict__ is d
assert get_true_dict(x) is not x.__dict__

d = get_true_dict(x)
d['a'] = 42
assert x.a == 42
d['b'] = 24
assert x.b == 24
print(d)

还有一个更晦涩的选项可以通过使用

ctypes
和偏移量来获取它,但它似乎只有在之前已经访问过
obj.__dict__
时才有效,否则返回
ValueError: PyObject is NULL
。除非有一些解决方法来初始化
__dict__
,否则此解决方案的缺陷违背了
get_true_dict
方法的全部目的。

def get_true_dict(obj: object) -> dict:
    import ctypes
    # -24 = -48 + 24
    offset = type(obj).__dictoffset__ + obj.__sizeof__()
    res = ctypes.py_object.from_address(id(obj) + offset)
    return res.value

class X: ...
x = X()
x.__dict__ # <<< comment this and it will change the behaviour and will result in
# ValueError: PyObject is NULL too

x.a = 42
assert get_true_dict(x) is x.__dict__

d = {}
class X:
    __dict__ = d
x = X()
x.a = 42
assert x.__dict__ is d
# ValueError: PyObject is NULL
assert get_true_dict(x) is not x.__dict__
d = get_true_dict(x)

PS 代码片段,作者:@denballakh

© www.soinside.com 2019 - 2024. All rights reserved.