做组合别名的 Pythonic 方式

Gra*_*rus 8 python

做组合别名的最pythonic和正确的方法是什么?

这是一个假设的场景:

class House:
    def cleanup(self, arg1, arg2, kwarg1=False):
        # do something

class Person:
    def __init__(self, house): 
        self.house = house
        # aliases house.cleanup
        # 1.
        self.cleanup_house = self.house.cleanup

    # 2.
    def cleanup_house(self, arg1, arg2, kwarg1=False):
        return self.house.cleanup(arg1=arg1, arg2=arg2, kwarg1=kwarg1)

Run Code Online (Sandbox Code Playgroud)

AFAIK 与 #1 我测试的编辑器理解这些和 #2 一样好 - 自动完成、文档字符串等。

#1 方法有什么缺点吗?从python的角度来看,哪种方式更正确?

扩展方法 #1 unsettable 和类型提示的变体将不受评论中指出的所有问题的影响:

class House:

    def cleanup(self, arg1, arg2, kwarg1=False):
        """clean house is nice to live in!"""
        pass


class Person:
    def __init__(self, house: House):
        self._house = house
        # aliases
        self.cleanup_house = self.house.cleanup

    @property
    def house(self):
        return self._house
Run Code Online (Sandbox Code Playgroud)

Mad*_*ist 8

第一种方法有很多问题:

  1. 除非您跳过额外的环节,否则当它引用的属性发生变化时,别名不会更新。例如,您可以使用 setter制作house一个property,但对于不需要它的东西来说,这是一项重要的工作。有关示例实现,请参阅此答案的结尾。
  2. cleanup_house不会被继承。在类中定义的函数对象是一个非数据描述符,可以被继承和覆盖,也可以绑定到一个实例。类中根本不存在第一种方法中的实例属性。它是一个绑定方法的事实是偶然的。super().cleanup_house对于一个具体的例子,子类将无法访问。
  3. person.cleanup_house.__name__ != 'cleanup_house'. 这不是您经常检查的内容,但是当您这样做时,您会期望函数名称是cleanup.

好消息是您不必多次重复签名即可使用方法#2。Python 提供了非常方便的 splat ( *)/splatty-splat ( **) 表示法,用于将所有参数检查委托给被包装的方法:

def cleanup_house(self, *args, **kwargs):
    return self.house.cleanup(*args, **kwargs)
Run Code Online (Sandbox Code Playgroud)

就是这样。所有常规和默认参数都按原样传递。

这就是 #2 是迄今为止更 Pythonic 的方法的原因。除非您复制方法签名,否则我不知道它将如何与支持类型提示的编辑器交互。

可能有问题的一件事cleanup_house.__doc__是与house.cleanup.__doc__. 这可能值得转换house为 a property,其 setter 分配cleanup_house.__doc__


要解决问题 1.(但不是 2. 或 3.),您可以house使用 setter 作为属性实现。这个想法是在house属性更改时更新别名。一般来说,这不是一个好主意,但这里是您在问题中所拥有的替代实现,它可能会更好地工作:

class House:
    def cleanup(self, arg1, arg2, kwarg1=False):
        """clean house is nice to live in!"""
        pass


class Person:
    def __init__(self, house: House):
        self.house = house  # use the property here

    @property
    def house(self):
        return self._house

    @house.setter
    def house(self, value):
        self._house = house
        self.cleanup_house = self._house.cleanup
Run Code Online (Sandbox Code Playgroud)

  • @花岗岩龙。我不确定 `*args` 和 `**kwargs` 有什么尴尬的。它们确实存在,因此您无需维护签名。`__doc__` 可以通过直接赋值来维护。 (2认同)