免责声明:this question引起了我的好奇心,我问这个纯粹是出于教育目的。我想,这里的Python专家面临更多挑战!
是否可以使type(foo)
的输出返回与实际实例类不同的值?即它可以冒充冒名顶替者并通过type(Foo()) is Bar
等支票吗?
@juanpa.arrivillaga建议在实例上手动重新分配__class__
,但这具有更改所有其他方法的调用方式的效果。例如
class Foo:
def test(self):
return 1
class Bar:
def test(self):
return 2
foo = Foo()
foo.__class__ = Bar
print(type(foo) is Bar)
print(foo.test())
>>> True
>>> 2
所需的输出为True
,1
。即type
中返回的类与实例不同,并且仍调用真实类中定义的实例方法。
答案 0 :(得分:3)
否-__class__
属性是有关在C API级别本身上“可见”的所有Python对象的布局的基本信息。这就是对type
的调用所检查的内容。
这意味着:每个Python对象在其内存布局中都有一个插槽,该插槽中有一个指向单个指针的空间,该指针指向该对象的类的Python对象。
即使您使用ctypes或其他方式来覆盖对该插槽的保护并从Python代码更改它(由于用obj.__class__
修改=
在C级别也受到保护),所以对其进行更改会有效地更改对象类型:__class__
槽中的值是对象的类,在您的示例中,test
方法将从那里的类(Bar)中选取。
但是这里有更多信息:在所有文档中,type(obj)
被视为与obj.__class__
等效-但是,如果对象的类定义了名称为__class__
的描述符,它将当使用格式obj.__class__
时使用。 type(obj)
但是将直接检查实例的__class__
插槽并返回真实的类。
因此,这可以“撒谎”使用obj.__class__
进行编码,而不是type(obj)
:
class Bar:
def test(self):
return 2
class Foo:
def test(self):
return 1
@property
def __class__(self):
return Bar
尝试在__class__
本身的元类上创建Foo
描述符会很麻烦-type(Foo())
和repr(Foo())
都将报告 instance < / em>为Bar
,但“真实”对象类为Foo。从某种意义上讲,是的,它使type(Foo())
处于撒谎状态,但不是您想的那样-type(Foo())将输出Bar()
的代表,但它是Foo
由于type.__call__
内部的实施细节,导致混乱的repr:
In [73]: class M(type):
...: @property
...: def __class__(cls):
...: return Bar
...:
In [74]: class Foo(metaclass=M):
...: def test(self):
...: return 1
...:
In [75]: type(Foo())
Out[75]: <__main__.Bar at 0x55665b000578>
In [76]: type(Foo()) is Bar
Out[76]: False
In [77]: type(Foo()) is Foo
Out[77]: True
In [78]: Foo
Out[78]: <__main__.Bar at 0x55665b000578>
In [79]: Foo().test()
Out[79]: 1
In [80]: Bar().test()
Out[80]: 2
In [81]: type(Foo())().test()
Out[81]: 1
type
本身由于没有人从任何地方“进口” type
,所以只能使用
内置类型本身,可以对内置类型进行猴子补丁
type
可用于举报错误的课程-该课程将对所有人有效
同一过程中的Python代码依赖于对type
的调用:
original_type = __builtins__["type"] if isinstance("__builtins__", dict) else __builtins__.type
def type(obj_or_name, bases=None, attrs=None, **kwargs):
if bases is not None:
return original_type(obj_or_name, bases, attrs, **kwargs)
if hasattr(obj_or_name, "__fakeclass__"):
return getattr(obj_or_name, "__fakeclass__")
return original_type(obj_or_name)
if isinstance(__builtins__, dict):
__builtins__["type"] = type
else:
__builtins__.type = type
del type
我在文档中没有找到一个窍门:在程序中访问__builtins__
时,它可以用作字典。但是,在诸如Python的Repl或Ipython之类的交互式环境中,它是一个
模块-检索原始type
并编写修改后的
__builtins__
的版本必须考虑到这一点-上面的代码
双向工作。
并对此进行测试(我从磁盘上的.py文件导入了上述代码段):
>>> class Bar:
... def test(self):
... return 2
...
>>> class Foo:
... def test(self):
... return 1
... __fakeclass__ = Bar
...
>>> type(Foo())
<class '__main__.Bar'>
>>>
>>> Foo().__class__
<class '__main__.Foo'>
>>> Foo().test()
1
尽管这是出于演示目的,但替换内置类型会导致“不和谐”,这种不和谐在更复杂的环境(如IPython)中被证明是致命的:如果运行上述代码段,Ipython将崩溃并立即终止。