8.2 使用内置的装饰器

    Python语言内置了一些装饰器。@property@classmethod@staticmethod装饰器用于标注类的方法。@property装饰器将一个方法函数转换成描述器。我们用这种简单属性语法来定义方法函数。当将@property装饰器用于方法上时,也会额外创建一对属性,它们可以用于创建setterdeleter属性。我们在第3章“属性访问、特性和修饰符”中讲解过这个部分。

    @classmethod@staticmethod装饰器将一个方法函数转换成一个类级函数。被装饰的方法现在可以用类调用,而不是对象。对于静态方法,没有显式的类引用。另一方面,对于类方法,类是该方法的第1个参数。下面是一个包含了@staticmethod和一些@property的类的例子。

    class Angle( float ):
      slots = ( "degrees", )
      @staticmethod
      def fromradians( value ):
        return Angle(180*value/math.pi)
      def new( cls, value ):
        self = super().__new
    (cls)
        self._degrees= value
        return self
      @property
      def radians( self ):
        return math.pi*self._degrees/180
      @property
      def degrees( self ):
        return self._degrees

    这个类定义了一个可以用度或者弧度表示的Angle类。构造器以度为参数。但是,我们也定义了fromradians()方法函数,它会返回一个本类的实例。这个函数不能通过实例调用,它属于类本身并返回一个当前类的实例。_new()方法是一个隐式的类方法,装饰器对它没有用。

    另外,我们提供了degrees()radians()方法函数,它们都被@propery所装饰,所以它们都是属性。其实,这些装饰器会创建一个描述器,这样当访问属性名degrees或者radians时,同名的方法函数会被调用。我们可以用static方法创建一个实例,然后通过property访问一个方法函数。

    >>> b=Angle.from_radians(.227)
    >>> b.degrees
    13.006141949469686

    静态方法实际上是一个函数,因为它与self实例变量无关。它的优点是它的语法直接与类绑定,用Angle.from_radians比调用一个名为angle_from_radians的函数更为直观。使用这些装饰器可以确保实现的正确性和一致性。

    使用标准库中的装饰器

    标准库中有许多装饰器。例如contextlibfunctoolsunittestatexitimportlibreprlib模块都包含了可以作为软件设计中横切方面的经典范例的装饰器。例如,functools库提供了totalordering装饰器,它定义了一系列比较运算符。它用eq()lt()le()gt()或者_ge()中的一个创建一套完整的比较运算。下面是一个只定义了两种比较运算的Card类。

    import functools
    @functools.totalordering
    class Card:
      slots = ( "rank", "suit" )
      def new( cls, rank, suit ):
        self = super().new(cls)
        self.rank= rank
        self.suit= suit
        return self
      def eq( self, other ):
        return self.rank == other.rank
      def _lt
    ( self, other ):
        return self.rank < other.rank

    我们的类被一个类级的装饰器所包装起来,@functools.total_ordering。这个装饰器会为我们创建未定义的方法函数。可以用这个类创建支持所有比较运算符的对象,虽然在类中只定义了两个。下面是使用我们定义的和未定义的比较运算符的例子。

    >>> c1= Card( 3, '♠' )
    >>> c2= Card( 3, '♥' )
    >>> c1 == c2
    True
    >>> c1 < c2
    False
    >>> c1 <= c2
    True
    >>> c1 >= c2
    True

    上面的代码展示了我们可以进行类中未定义的比较运算。装饰器将缺少的方法函数添加到原始类定义中。