是否可以使`type`的输出返回不同的类? [英] Is it possible to make the output of `type` return a different class?

查看:61
本文介绍了是否可以使`type`的输出返回不同的类?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

因此免责声明:此问题引起了我的好奇心,而我出于纯粹的教育目的而提出这个要求.我想,这里的Python专家还面临更多挑战!

So disclaimer: this question has piqued my curiosity a bit, and I'm asking this for purely educational purposes. More of a challenge for the Python gurus here I suppose!

是否可以使type(foo)的输出返回与实际实例类不同的值?即它可以冒充冒名顶替者并通过诸如type(Foo()) is Bar的支票吗?

Is it possible to make the output of type(foo) return a different value than the actual instance class? i.e. can it pose as an imposter and pass a check such as type(Foo()) is Bar?

@ juanpa.arrivillaga 建议在实例上手动重新分配__class__,但是具有更改所有其他方法的调用方式的效果.例如

@juanpa.arrivillaga made a suggestion of manually re-assigning __class__ on the instance, but that has the effect of changing how all other methods would be called. e.g.

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

所需的输出将是True1.即,type中返回的类与实例不同,并且仍然调用真实类中定义的实例方法.

The desired outputs would be True, 1. i.e The class returned in type is different than the instance, and the instance methods defined in the real class still get invoked.

推荐答案

否-__class__属性是有关在C API级别本身上可见"的所有Python对象的布局的基本信息.这就是通过调用type检查的内容.

No - the __class__ attribute is a fundamental information on the layout of all Python objects as "seen" on the C API level itself. And that is what is checked by the call to type.

这意味着:每个Python对象在其内存布局中都有一个插槽,该插槽中有一个指向单个指针的空间,该指针指向该对象的类的Python对象.

That means: every Python object have a slot in its in-memory layout with space for a single pointer, to the Python object that is that object's class.

即使您使用ctypes或其他方式来覆盖对该插槽的保护并从Python代码更改它(由于在C级别上保护了使用=修改obj.__class__),对其进行更改也可以有效地更改对象类型: __class__插槽中的值是对象的类,在您的示例中,将从该类(Bar)中的类中选择test方法.

Even if you use ctypes or other means to override protection to that slot and change it from Python code (since modifying obj.__class__ with = is guarded at the C level), changing it effectively changes the object type: the value in the __class__ slot IS the object's class, and the test method would be picked from the class in there (Bar) in your example.

但是这里有更多信息:在所有文档中,type(obj)被视为等同于obj.__class__-但是,如果对象的类定义了名称为__class__的描述符,则在使用表格obj.__class__. type(obj)但是将直接检查实例的__class__插槽并返回真实的类.

However there is more information here: in all documentation, type(obj) is regarded as equivalent as obj.__class__ - however, if the objects'class defines a descriptor with the name __class__, it is used when one uses the form obj.__class__. type(obj) however will check the instance's __class__ slot directly and return the true class.

因此,这可以撒谎"使用obj.__class__进行编码,而不能使用type(obj):

So, this can "lie" to code using obj.__class__, but not type(obj):

class Bar:
    def test(self):
        return 2

class Foo:
    def test(self):
        return 1
    @property
    def __class__(self):
        return Bar

元类的属性

试图弄乱在Foo本身的元类上创建__class__描述符会很麻烦-type(Foo())repr(Foo())都将报告实例,但是真实"对象类将是Foo.从某种意义上讲,是的,它使type(Foo())说谎,但不是您想的那样-type(Foo())将输出Bar()的repr,但是Foo的repr却被弄乱了由于type.__call__中的实现细节:

Property on the metaclass

Trying to mess with creating a __class__ descriptor on the metaclass of Foo itself will be messy -- both type(Foo()) and repr(Foo()) will report an instance of Bar, but the "real" object class will be Foo. In a sense, yes, it makes type(Foo()) lie, but not in the way you were thinking about - type(Foo()) will output the repr of Bar(), but it is Foo's repr that is messed up, due to implementation details inside 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本身

由于没有人从任何地方导入" type,因此只能使用 内置类型本身,可以对内置类型进行猴子补丁 type可调用以报告错误的类-它将对所有人有效 相同过程中的Python代码依赖于对type的调用:

Modifying type itself

Since no one "imports" type from anywhere, and just use the built-in type itself, it is possible to monkeypatch the builtin type callable to report a false class - and it will work for all Python code in the same process relying on the call to 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__的版本必须考虑到这一点-上面的代码 双向工作.

There is one trick here I had not find in the docs: when acessing __builtins__ in a program, it works as a dictionary. However, in an interactive environment such as Python's Repl or Ipython, it is a module - retrieving the original type and writting the modified version to __builtins__ have to take that into account - the code above works both ways.

并对此进行测试(我从磁盘上的.py文件导入了上述代码段):

And testing this (I imported the snippet above from a .py file on disk):

>>> 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将崩溃并立即终止.

Although this works for demonstration purposes, replacing the built-in type caused "dissonances" that proved fatal in a more complex environment such as IPython: Ipython will crash and terminate immediately if the snippet above is run.

这篇关于是否可以使`type`的输出返回不同的类?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆