ent*_*iae 3 python abstract-base-class pycharm python-3.6
在Python 3.6中,我试图在AbstractBaseClass中定义一个属性; 我的第一次尝试是这样的(后来我发现我可以省略@staticmethod):
class AnAbstractClass(ABC):
@property
@staticmethod
@abstractmethod
def my_property():
pass
Run Code Online (Sandbox Code Playgroud)
据我所知,@staticmethod装饰器不返回可调用但不同的东西.(我怀疑这也导致mypy在我的代码中的返回值类型上引发错误,但是我无法在较小的代码示例中重现该问题).
这里发生了什么?
装饰
装饰器是一个可调用的,它可以替换它正在装饰的东西,并将它分配给命名空间中的相同名称.通常,装饰器用于函数和类来添加一些功能,比如类型检查或线程或其他东西,但实际上它们可以返回任何东西.
由于装饰器的输出不必与输入的类型相同,或者执行任何相同的处理,因此装饰器的顺序非常重要.装饰器按从最靠近函数的那个到列表顶部的那个顺序应用.abstractmethod那么staticmethod,在你的情况下,那么property.
叙
描述符定义了一个相当复杂的协议,允许使用它们提供的绑定行为来定制对象.出于您的目的,您需要知道函数是描述符,并且将它们放入类对象中使用它.当您调用在该类的实例上的类中定义的任何描述符时,描述符协议使用描述符的__get__方法将描述符绑定到实例.描述符本身甚至不必是可调用的,也不是返回值__get__,即使在大多数情况下它是预期的.对于函数,__get__返回一个自动self作为第一个位置参数传递的闭包.
例如,给定一个A带有方法的类def b(self, arg):,以及调用该类的实例a,进行a.b(arg)转换A.b.__get__(a, A)(arg).因此,虽然b定义了两个位置参数,但只需要在实例上调用时显式传递一个.但是,当您b通过类调用时,例如,A.b(a, arg)它只是一个普通函数,您需要手动传递所有参数,包括self.
把它们放在一起
abstractmethod,property并且staticmethod都是返回可调用描述符对象的装饰器.然而,__get__它们的描述符的方法与__get__普通函数对象的工作方式略有不同.
abstractmethod创建一个相当普通的类方法,但它与元类交互ABCMeta,以便在尝试使用抽象方法实例化类时获得各种有用的错误.它不会以任何方式修改结果所期望的输入参数.事实上,文档暗示所有副作用可能与元类相关,并且原始输入只是通过.这里唯一要记住的是
当
abstractmethod()与其他方法描述符结合使用时,它应该作为最里面的装饰器应用,如以下用法示例所示:...
你的代码似乎遵循了这个禁令.事实上abstractmethod与你的警告无关,但无论如何在这里提一下似乎是个好主意.
staticmethod返回一个绕过正常绑定行为的可调用对象,以创建一个不关心调用它的类或实例的方法.特别是,使用绑定的方法staticmethod.__get__会将其参数传递给您的函数,而不是self首先进行前置(即,__get__基本上只返回原始函数).你可以想象这对于期望接收self参数的东西来说是一个问题,比如属性的setter.
与abstractmethod和不同staticmethod,property创建数据描述符.这意味着它返回一个既具有__get__绑定又具有__set__绑定(以及__del__绑定)的对象.属性的__get__方法与普通函数的__get__方法非常相似,但专门应用于getter函数.property非常关心它是从哪个实例调用的,因为当然你希望不同的实例具有属性包装的属性的不同值.
所以,你必须在你的代码是什么staticmethod之后property.第一个装饰器返回一个函数,self该函数在绑定时不会在其参数列表前面添加,而第二个装饰器需要一个函数.没有什么可以阻止你调用装饰器,但IDE警告告诉你,你不会成功调用结果对象.如果你试图访问my_property一个具体的实现AnyAbstractClass,你可能会TypeError告诉你my_property不接受任何位置参数,但是给出了一个,因为property.__get__它将self在静态方法的参数列表前面,它不接受任何参数.
请记住,申请staticmethod结果对property你也没有多大帮助.一个property实例是不可调用的.它完全是通过其工作__get__,__set__和__del__方法,同时staticmethod假定您在调用传递.
解
正如你已经正确发现的那样,staticmethod并且property不能很好地混合.就其本质而言,属性应始终了解其运行的实例.执行此操作的正确方法是添加self参数并允许进行常规方法绑定.
两者property并staticmethod用发挥好abstractmethod(只要abstractmethod施加第一),因为它使有效不改变原来的功能.实际上,abstractmethod特别提到property摘要的getter,setter或deleter 使得整个属性都是抽象的文档.
TL; DR
staticmethod返回一个可调用的描述符,但其__get__方法返回自身的未绑定版本.property创建一个不可__get__调用的描述符,其方法调用属性的getter.使用result属性将尝试传递self给不接受它的静态方法.