是否可以使 `type` return 的输出不同 class?
Is it possible to make the output of `type` return a different class?
所以免责声明: 有点激起了我的好奇心,我问这个纯粹是出于教育目的。我想这里对 Python 专家来说是一个更大的挑战!
是否可以使 type(foo)
return 的输出值与实际实例 class 的值不同?即它可以伪装成冒名顶替者并通过 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
中的 class return 与实例不同,真正的 class 中定义的实例方法仍然会被调用。
否 - __class__
属性是有关所有 Python 对象布局的基本信息,如 "seen" 在 C API 级别本身。这就是调用 type
.
所检查的内容
这意味着:每个 Python 对象在其 in-memory 布局中都有一个插槽,其中 space 用于单个指针,指向 Python 对象,即该对象的 class。
即使您使用 ctypes 或其他方式覆盖对该插槽的保护并将其从 Python 代码更改(因为使用 =
修改 obj.__class__
在 C 级别受到保护) ,更改它会有效地更改对象类型:__class__
槽中的值是对象的 class,并且 test
方法将从那里的 class 中选取(Bar ) 在你的例子中。
但是这里有更多信息:在所有文档中,type(obj)
被视为等同于 obj.__class__
- 但是,如果对象的 class 定义了一个名称为 __class__
,用在obj.__class__
的形式。 type(obj)
但是会直接检查实例的 __class__
插槽和 return 真正的 class.
因此,这可以 "lie" 使用 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
的 metaclass 上创建一个 __class__
描述符本身会很麻烦——type(Foo())
和 repr(Foo())
都会报告 Bar
的 实例 ,但 "real" 对象 class 将是 Foo。从某种意义上说,是的,它让 type(Foo())
撒谎,但不是你想的那样 - type(Foo()) 将输出 Bar()
的 repr,但它是 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
本身
因为任何地方都没有人 "imports" type
,所以只需使用
built-in 类型本身,可以对内置的进行猴子修补
type
可调用以报告错误 class - 它适用于所有人
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
尽管这适用于演示目的,但替换导致 "dissonances" 的 built-in 类型在 IPython 等更复杂的环境中证明是致命的:Ipython 将崩溃并且如果上面的代码片段是 运行,则立即终止。
所以免责声明:
是否可以使 type(foo)
return 的输出值与实际实例 class 的值不同?即它可以伪装成冒名顶替者并通过 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
中的 class return 与实例不同,真正的 class 中定义的实例方法仍然会被调用。
否 - __class__
属性是有关所有 Python 对象布局的基本信息,如 "seen" 在 C API 级别本身。这就是调用 type
.
这意味着:每个 Python 对象在其 in-memory 布局中都有一个插槽,其中 space 用于单个指针,指向 Python 对象,即该对象的 class。
即使您使用 ctypes 或其他方式覆盖对该插槽的保护并将其从 Python 代码更改(因为使用 =
修改 obj.__class__
在 C 级别受到保护) ,更改它会有效地更改对象类型:__class__
槽中的值是对象的 class,并且 test
方法将从那里的 class 中选取(Bar ) 在你的例子中。
但是这里有更多信息:在所有文档中,type(obj)
被视为等同于 obj.__class__
- 但是,如果对象的 class 定义了一个名称为 __class__
,用在obj.__class__
的形式。 type(obj)
但是会直接检查实例的 __class__
插槽和 return 真正的 class.
因此,这可以 "lie" 使用 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
的 metaclass 上创建一个 __class__
描述符本身会很麻烦——type(Foo())
和 repr(Foo())
都会报告 Bar
的 实例 ,但 "real" 对象 class 将是 Foo。从某种意义上说,是的,它让 type(Foo())
撒谎,但不是你想的那样 - type(Foo()) 将输出 Bar()
的 repr,但它是 Foo
由于 type.__call__
:
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
本身
因为任何地方都没有人 "imports" type
,所以只需使用
built-in 类型本身,可以对内置的进行猴子修补
type
可调用以报告错误 class - 它适用于所有人
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
尽管这适用于演示目的,但替换导致 "dissonances" 的 built-in 类型在 IPython 等更复杂的环境中证明是致命的:Ipython 将崩溃并且如果上面的代码片段是 运行,则立即终止。