python – @staticmethod返回值


Python 3.6中,我试图在AbstractBaseClass中定义一个属性;我的第一次尝试是这样的(后来我发现我可以省略@staticmethod):

class AnAbstractClass(ABC):
    @property
    @staticmethod
    @abstractmethod
    def my_property():
        pass

但是,PyCharm向我展示了对房产装饰者的警告:
《python – @staticmethod返回值》

据我所知,@ staticmethod装饰器不会返回可调用但不同的东西.
(我怀疑这也导致mypy在我的代码中的返回值类型上引发错误,但是我无法在较小的代码示例中重现该问题).

这里发生了什么?

最佳答案 要了解您获得警告的原因,您需要了解
decorators
descriptors.

装饰

装饰器是一个可调用的,它可以替换它正在装饰的东西,并将它分配给命名空间中的相同名称.通常,装饰器用于函数和类来添加一些功能,比如类型检查或线程或其他东西,但实际上它们可以返回任何东西.

由于装饰器的输出不必与输入的类型相同,或者执行任何相同的处理,因此装饰器的顺序非常重要.装饰器按照从最靠近函数的那个​​到列表顶部的那个顺序应用.在你的情况下,abstractmethod,然后staticmethod,然后属性.

描述符定义了一个相当复杂的协议,允许使用它们提供的绑定行为来定制对象.出于您的目的,您需要知道函数是描述符,并且将它们放入类对象中使用它.当您调用在该类的实例上的类中定义的任何描述符时,描述符协议使用描述符的__get__方法将描述符绑定到实例.描述符本身甚至不必是可调用的,也不是__get__的返回值,即使在大多数情况下它是预期的.对于函数,__ get__返回一个闭包,该闭包自动将self作为第一个位置参数传递.

例如,给定一个带有方法def b(self,arg):的类A,以及一个名为a的类的实例,执行a.b(arg)将变为A.b .__ get __(a,A)(arg).因此,虽然b被定义为具有两个位置参数,但是当在实例上调用时,它只需要一个明确地传递.但是,当您通过类调用b时,例如A.b(a,arg),它只是一个普通函数,您需要手动传递所有参数,包括self.

把它们放在一起

abstractmethod,propertystaticmethod都是返回可调用描述符对象的装饰器.但是,它们的decriptors的__get__方法与普通函数对象的__get__有点不同.

abstractmethod创建了一个相当普通的类方法,但它与元类ABCMeta交互,因此当您尝试使用抽象方法实例化类时,会遇到各种有用的错误.它不会以任何方式修改结果所期望的输入参数.事实上,文档暗示所有副作用可能与元类相关,并且原始输入只是通过.这里唯一要记住的是

When abstractmethod() is applied in combination with other method descriptors, it should be applied as the innermost decorator, as shown in the following usage examples: …

你的代码似乎遵循了这个禁令.事实上,抽象方法与你的警告无关,但无论如何在这里提一下似乎是个好主意.

staticmethod返回一个绕过正常绑定行为的可调用对象,以创建一个不关心调用它的类或实例的方法.特别是,使用staticmethod .__ get__绑定的方法会将其参数传递给您的函数,而不是先自我预设(即,__ get__基本上只返回原始函数).你可以想象这对于希望接收自身参数的东西来说是一个问题,比如属性的setter.

与abstractmethod和staticmethod不同,属性创建数据描述符.这意味着它返回一个同时具有__get__绑定和__set__绑定(以及__del__绑定)的对象.属性的__get__方法与普通函数的__get__方法非常相似,但专门应用于getter函数.属性非常关心它被调用的实例,因为当然你希望不同的实例具有属性包装的属性的不同值.

所以你的代码中包含的是staticmethod,后跟属性.第一个装饰器返回一个函数,该函数在绑定时不会将self添加到其参数列表中,而第二个装饰器则执行.没有什么可以阻止你调用装饰器,但IDE警告告诉你,你不会成功调用结果对象.如果您尝试在AnyAbstractClass的具体实现上访问my_property,您可能会得到一个TypeError,告诉您my_property不接受任何位置参数,但是给出了一个,因为property .__ get__会将self添加到静态方法的参数列表中,不接受任何论点.

请记住,将staticmethod应用于属性的结果也不会对您有所帮助.属性实例根本不可调用.它完全通过__get __,__ set__和__del__方法运行,而staticmethod假定你传入一个可调用的方法.

正如您已经正确发现的那样,静态方法和属性不能很好地混合.就其本质而言,属性应始终了解其运行的实例.执行此操作的正确方法是添加自身参数并允许进行常规方法绑定.

属性和staticmethod都可以很好地与abstractmethod一起使用(只要首先应用abstractmethod),因为它实际上不会改变你的原始函数.事实上,abstractmethod的文档特别提到属性abstract的getter,setter或deleter使整个属性变得抽象.

TL; DR

staticmethod返回一个可调用的描述符,但其__get__方法返回其自身的未绑定版本. property创建一个不可调用的描述符,其__get__方法调用属性的getter.使用结果属性将尝试将self传递给不接受它的静态方法.

点赞