gpt4 book ai didi

python - 复制内置类

转载 作者:太空宇宙 更新时间:2023-11-04 02:26:49 24 4
gpt4 key购买 nike

我正在尝试编写从类创建类而不修改原始类的函数。

简单的解决方案(基于 this answer )

def class_operator(cls):
namespace = dict(vars(cls))
... # modifying namespace
return type(cls.__qualname__, cls.__bases__, namespace)

工作正常,除了 type 本身:

>>> class_operator(type)
Traceback (most recent call last):
File "<input>", line 1, in <module>
TypeError: type __qualname__ must be a str, not getset_descriptor

Python 3.2-Python 3.6 上测试。

(我知道在当前版本中修改 namespace 对象中的可变属性会改变原始类,但事实并非如此)

更新

即使我们从 namespace 中删除 __qualname__ 参数(如果有的话)

def class_operator(cls):
namespace = dict(vars(cls))
namespace.pop('__qualname__', None)
return type(cls.__qualname__, cls.__bases__, namespace)

结果对象的行为与原始类型不同

>>> type_copy = class_operator(type)
>>> type_copy is type
False
>>> type_copy('')
Traceback (most recent call last):
File "<input>", line 1, in <module>
TypeError: descriptor '__init__' for 'type' objects doesn't apply to 'type' object
>>> type_copy('empty', (), {})
Traceback (most recent call last):
File "<input>", line 1, in <module>
TypeError: descriptor '__init__' for 'type' objects doesn't apply to 'type' object

为什么?

谁能解释一下 Python 内部的什么机制可以防止复制 type 类(以及许多其他内置类)。

最佳答案

这里的问题是 type有一个 __qualname__在其 __dict__ ,这是一个属性(即 descriptor )而不是字符串:

>>> type.__qualname__
'type'
>>> vars(type)['__qualname__']
<attribute '__qualname__' of 'type' objects>

并尝试将非字符串分配给 __qualname__一个类的抛出异常:

>>> class C: pass
...
>>> C.__qualname__ = 'Foo' # works
>>> C.__qualname__ = 3 # doesn't work
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: can only assign string to C.__qualname__, not 'int'

这就是为什么有必要删除 __qualname__来自 __dict__ .

至于你的type_copy的原因不可调用:这是因为 type.__call__拒绝任何不是 type 子类的东西.对于 3 参数形式都是如此:

>>> type.__call__(type, 'x', (), {})
<class '__main__.x'>
>>> type.__call__(type_copy, 'x', (), {})
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: descriptor '__init__' for 'type' objects doesn't apply to 'type' object

以及单参数形式,它实际上只适用于 type作为它的第一个参数:

>>> type.__call__(type, 3)
<class 'int'>
>>> type.__call__(type_copy, 3)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: type.__new__() takes exactly 3 arguments (1 given)

这不容易规避。修复 3-argument 形式很简单:我们将副本设为 type 的空子类。 .

>>> type_copy = type('type_copy', (type,), {})
>>> type_copy('MyClass', (), {})
<class '__main__.MyClass'>

但是type的单参数形式更麻烦,因为它只有在第一个参数是 type 时才有效.我们可以实现自定义 __call__方法,但是那个方法必须写在元类中,也就是type(type_copy)将不同于 type(type) .

>>> class TypeCopyMeta(type):
... def __call__(self, *args):
... if len(args) == 1:
... return type(*args)
... return super().__call__(*args)
...
>>> type_copy = TypeCopyMeta('type_copy', (type,), {})
>>> type_copy(3) # works
<class 'int'>
>>> type_copy('MyClass', (), {}) # also works
<class '__main__.MyClass'>
>>> type(type), type(type_copy) # but they're not identical
(<class 'type'>, <class '__main__.TypeCopyMeta'>)

type 有两个原因很难复制:

  1. 它是用 C 实现的。如果你尝试复制其他内置类型,如 int,你会遇到类似的问题。或 str .
  2. 事实 type自身的实例:

    >>> type(type)
    <class 'type'>

    这通常是不可能的。它模糊了类和实例之间的界限。它是实例和类属性的困惑堆积。这就是为什么 __qualname__当作为 type.__qualname__ 访问时是一个字符串但是当作为 vars(type)['__qualname__'] 访问时是一个描述符.


如您所见,不可能完美复制 type .每个实现都有不同的权衡。

简单的解决方案是创建 type 的子类,它不支持单参数 type(some_object)调用:

import builtins

def copy_class(cls):
# if it's a builtin class, copy it by subclassing
if getattr(builtins, cls.__name__, None) is cls:
namespace = {}
bases = (cls,)
else:
namespace = dict(vars(cls))
bases = cls.__bases__

cls_copy = type(cls.__name__, bases, namespace)
cls_copy.__qualname__ = cls.__qualname__
return cls_copy

精心设计的解决方案是制作自定义元类:

import builtins

def copy_class(cls):
if cls is type:
namespace = {}
bases = (cls,)

class metaclass(type):
def __call__(self, *args):
if len(args) == 1:
return type(*args)
return super().__call__(*args)

metaclass.__name__ = type.__name__
metaclass.__qualname__ = type.__qualname__
# if it's a builtin class, copy it by subclassing
elif getattr(builtins, cls.__name__, None) is cls:
namespace = {}
bases = (cls,)
metaclass = type
else:
namespace = dict(vars(cls))
bases = cls.__bases__
metaclass = type

cls_copy = metaclass(cls.__name__, bases, namespace)
cls_copy.__qualname__ = cls.__qualname__
return cls_copy

关于python - 复制内置类,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50175903/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com