Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/python-3.x/18.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 类名称作为幂等typecast函数_Python_Python 3.x_Constructor_Casting - Fatal编程技术网

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
模式很整洁,没有想到这一点。我想我还是会为此编写一个元类,以便将此检查自动引入派生类的构造函数中。