在 Python 中通过传递相同类型的对象来初始化类实例

Initialize a class instance in Python by passing an object of the same type

提问人:opnightfall1771 提问时间:10/29/2021 更新时间:10/29/2021 访问量:759

问:

在 Python 的 pathlib 模块中,您可以从另一个 Path 创建一个 Path 对象,它们将是等效的:

p = Path('some/path')
p2 = Path(p)
p == p2
# True

我有一个 File 类,我想实现相同的行为,如果我将 File 对象传递给 File 构造函数,它将只返回传入的原始对象。正确的方法是什么?我是否需要重写该方法来实现此目的?__new__

评论

4赞 juanpa.arrivillaga 10/29/2021
“如果我将 File 对象传递给 File 构造函数,它只会返回传入的原始对象。” 您会注意到,这实际上不是上面发生的事情,将计算结果为 .它们是相等的,但并不完全相同。虽然,您将看到此行为的一个例子是字符串,即 恰好在 CPython 中是正确的。p is p2Falsestr(some_str) is some_str
3赞 d.b 10/29/2021
如果很简单,您可以提取数据并创建一个新实例。或者,您可以检查类型并制作深度副本。
0赞 opnightfall1771 10/31/2021
@d.b 谢谢!这很有帮助。我遵循了这个解决方案。

答:

1赞 jdaz 10/29/2021 #1

正如 @d.b 在评论中提到的,您可以“提取数据并创建一个新实例”。这恰好是如何使用 s。这是来自源代码的关键函数(为清楚起见,此处简化),该函数从以下位置调用:Path@classmethod__new__

@classmethod
def _parse_args(cls, args):
    parts = []
    for a in args:
        if isinstance(a, PurePath): # <--- Here
            parts += a._parts       # <---
        else:
            a = os.fspath(a)
            if isinstance(a, str):
                parts.append(str(a))
            else:
                raise TypeError()
    return cls._flavour.parse_parts(parts)

然后返回,因为类定义了 ,尽管它不是原始对象。p == p2True__eq__p2

评论

0赞 opnightfall1771 10/31/2021
很有帮助,谢谢!我最终实现了一个解决方案,提取数据并创建一个新实例。
1赞 nadapez 10/29/2021 #2

如果你真的希望构造函数返回原始对象,那么你必须重写方法:__new__

def __new__(cls, *args):
    if isinstance(args[0], File):
        return args[0]
    instance = super().__new__(cls, *args)
    # do initialization
    return instance

请注意,您不能使用,因为即使没有创建新实例,也会调用它。__init__

一个更简洁的解决方案是使用返回实例或新实例的函数:

def File(*args):
    if isinstance (args[0], File_):
        return args[0]
    return File_(*args)

原始类在哪里File_