是否可以在 Python 中覆盖命名元组中的方法?

Posted

技术标签:

【中文标题】是否可以在 Python 中覆盖命名元组中的方法?【英文标题】:Is it possible to override a method in a namedtuple in Python? 【发布时间】:2015-12-23 11:29:53 【问题描述】:

假设我有这样的课程:

class Foo(namedtuple('Foo', ['f1'])):
    def f1(self):
        print('Default f1')

    def __new__(cls, f1=f1):
        return super().__new__(cls, f1)

假设我稍后创建了一个 Foo 对象并选择覆盖 f1 的方法定义,如下所示:

def my_f1():
    print("My f1")

foo = Foo(f1=my_f1)

如果我再尝试:

foo.f1()

我明白了:

Default f1

当然,我正在尝试打印“My f1”。我也希望 f1 是可选的。这里发生了什么?是否可以在命名元组中为方法定义默认实现,然后在 new 中覆盖它?

【问题讨论】:

在我看来,您想要一个具有默认值的字段命名元组。在这种情况下,默认值是可调用的,但没有太大区别:***.com/questions/11351032/… @AndreaCorbellini:这有很大的不同,因为处理属性的属性被方法破坏了。 @MartijnPieters:只有当你想在 class Foo 中声明 f1 时才会有所不同。但这会使事情过于复杂(有关详细信息,请参阅您自己的答案:P)。在 class Foo 之外声明 f1 并将其设置为默认参数会使事情变得更容易。 @AndreaCorbellini:但是 OP 期望 foo.f1() 在创建实例时从 f1 参数中工作。 self[0] 是默认的 None 还是默认的外部函数都没有关系.. @MartijnPieters:我不确定我是否理解您的最后评论。顺便说一句,我发布了一个答案。 【参考方案1】:

您不能让方法和槽使用相同的名称。命名元组使用slots,这些被实现为property 对象在与方法相同的命名空间中。通过定义一个方法 f1 你破坏了 f1 属性:

>>> from collections import namedtuple
>>> namedtuple('Foo', ['f1']).f1
<property object at 0x1069764c8>
>>> class Foo(namedtuple('Foo', ['f1'])):
...     def f1(self):
...         print('Default f1')
... 
>>> Foo.f1
<unbound method Foo.f1>

property 对象只返回 self[0]。所以要么按位置访问f1 值:

class Foo(namedtuple('Foo', ['f1'])):
    def f1(self):
        if self[0]:
            return self[0]()
        print('Default f1')

或者给你的属性一个不同的名字,然后让f1方法委托给它:

class Foo(namedtuple('Foo', ['f1_callable'])):
    def f1(self):
        if self.f1_callable:
            return self.f1_callable()
        print('Default f1')

演示:

>>> def my_f1():
...     print("My f1")
... 
>>> class Foo(namedtuple('Foo', ['f1'])):
...     def f1(self):
...         if self[0]:
...             return self[0]()
...         print('Default f1')
... 
>>> foo = Foo(my_f1)
>>> foo.f1()
My f1
>>> foo = Foo(None)
>>> foo.f1()
Default f1

【讨论】:

【参考方案2】:

避免在class Foo 中声明f1。相反,在模块级别声明它,并将其用作default argument for your namedtuple:

def f1():
    print('Default f1')

Foo = namedtuple('Foo', 'f1')
Foo.__new__.__defaults__ = (f1,)

用法:

>>> foo = Foo()
>>> foo.f1()
Default f1

>>> def other_f1():
...     print('Another f1')
>>> foo = Foo(f1=other_f1)
>>> foo.f1()
Another f1

【讨论】:

以上是关于是否可以在 Python 中覆盖命名元组中的方法?的主要内容,如果未能解决你的问题,请参考以下文章

python学习--为元组中每个元素命名

Python冷知识之命名元组

为元组中的每个元素命名,提高程序可读性

python_如何为元组中每个元素命名

python 可命名元组

Python中的元组(Tuple)