Python 类名称作为幂等typecast函数
我正在编写一个自定义类型Python 类名称作为幂等typecast函数,python,python-3.x,constructor,casting,Python,Python 3.x,Constructor,Casting,我正在编写一个自定义类型Foo,我希望实现以下目标:在编写 foo = Foo(bar) 然后,如果bar已经是Foo的实例,则Foo(Foo)返回未修改的对象,因此Foo和bar引用相同的对象。否则,应使用bar中的信息以Foo.\uuuu init\uuuu认为合适的任何方式创建Foo类型的新对象 我该怎么做 我认为Foo.\uuuuu new\uuuuu是这一点的关键。如果instanceof检查成功,那么让Foo.\uuuuuuu new\uuuuuu返回现有对象应该相当容易,否则调用
Foo
,我希望实现以下目标:在编写
foo = Foo(bar)
然后,如果bar
已经是Foo
的实例,则Foo(Foo)
返回未修改的对象,因此Foo
和bar
引用相同的对象。否则,应使用bar
中的信息以Foo.\uuuu init\uuuu
认为合适的任何方式创建Foo
类型的新对象
我该怎么做
我认为Foo.\uuuuu new\uuuuu
是这一点的关键。如果instanceof
检查成功,那么让Foo.\uuuuuuu new\uuuuuu
返回现有对象应该相当容易,否则调用super()。但他写道:
如果\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
在本例中,我将返回所请求类的一个实例,尽管不是一个新实例。我能否以某种方式阻止对\uuuuu init\uuuuu
的调用?或者我必须在\uuuu init\uuuu
中添加一个检查,以检测是为新创建的实例还是为已经存在的实例调用它?后者听起来像是应该避免的代码重复。我想,你应该直接使用\uuuuuu新的\uuuuuuu
和\uuuuu初始\uuuu
。在init中测试是应该初始化一个新对象还是已经有一个现有对象非常简单,因此没有代码重复,增加的复杂性是可以接受的
class Foo:
def __new__(cls, obj):
if isinstance(obj, cls):
print("New: same object")
return obj
else:
print("New: create object")
return super(Foo, cls).__new__(cls)
def __init__(self, obj):
if self is obj:
print("init: same object")
else:
print("init: brand new object from ", obj)
# do the actual initialization
正如预期的那样:
>>> foo = Foo("x")
New: create object
init: brand new object from x
>>> bar = Foo(foo)
New: same object
init: same object
>>> bar is foo
True
实现这一点的一种方法是将所需代码移动到元类中,如下所示:
class IdempotentCast(type):
"""Metaclass to ensure that Foo(x) is x if isinstance(x, Foo)"""
def __new__(cls, name, bases, namespace, **kwds):
res = None
defineNew = all(i.__new__ is object.__new__ for i in bases)
if defineNew:
def n(cls, *args, **kwds):
if len(args) == 1 and isinstance(args[0], cls) and not kwds:
return args[0]
else:
return super(res, cls).__new__(cls)
namespace["__new__"] = n
realInit = namespace.get("__init__")
if realInit is not None or defineNew:
@functools.wraps(realInit)
def i(self, *args, **kwds):
if len(args) != 1 or args[0] is not self:
if realInit is None:
return super(res, self).__init__(*args, **kwds)
else:
return realInit(self, *args, **kwds)
namespace["__init__"] = i
res = type.__new__(cls, name, bases, namespace)
return res
class Foo(metaclass=IdempotentCast):
...
该元类添加了一个\uuuuuuuuuuuuuuuuuuuuuuu
方法,除非存在一个已经添加了\uuuuuuuuuuuuuuuuuuu新方法的基类。因此,对于某些类扩展另一个类的类层次结构,只会添加一次\uuuu new\uuuu
方法。它还包装构造函数以执行第一个参数是否与self
相同的检查(感谢by指出这个简单的检查)。否则,它将调用原始构造函数,如果没有定义构造函数,则调用基构造函数
相当多的代码,但您只需要一次,并且可以使用它为任意多的类型引入这些语义。如果你只需要一个类,其他答案可能更合适。你不允许使用工厂方法来实现这一点吗?@Tagc:我有点习惯于使用构造函数名进行强制转换。例如float(x)
、str(x)
或tuple(x)
如果对象的类型已经正确,则返回相同的对象。我经常使用这种符号,所以我希望我自己的类型也使用这种符号,保持一致。我想这是一个风格的问题。从功能上讲,工厂方法当然也可以。self is arg
模式很整洁,没有想到这一点。我想我还是会为此编写一个元类,以便将此检查自动引入派生类的构造函数中。