访问器方法来设置、获取对象的属性

class Rectangle: def init(self): self.width = 0 self.height = 0 def setSize(self, size): self.width, self.height = size def getSzie(self): return self.width, self.height

对于这个类,有width和height两个属性

我们可以通过setSize方法,接受size元组来给这两个属性赋值

可以通过getSize方法,以元组的形式获取这两个属性

但是每个操作都要有一个方法对应,有没有简单的方法来访问对象的属性呢?

property函数来操作对象的属性

只能用于新式类
property函数可以接受零个、一个、三个、或四个参数
其原型为property(fget, fset, fdel, doc)

  • fget参数接受一个获取属性的方法
  • fset参数接受一个设置属性的方法
  • fdel参数接受一个删除属性的方法
  • doc参数接受一个文档字符串
    如果不按顺序给出参数,可以采用关键词参数
In [1]:
__metaclass__=type
In [3]:
class Rectangle:
    def __init__(self):
        self.width = 0
        self.height = 0
    def setSize(self, size):
        self.width, self.height = size
    def getSize(self):
        return self.width, self.height
    size = property(getSize, setSize)          #注意这行代码,这样我们就可以简单地对size属性进行赋值和获取,相关的方法会自动调用
In [4]:
r = Rectangle()
In [6]:
r.size = 150, 100           #我们可以直接给属性赋值,setSize方法会被自动调用
In [7]:
r.size           #我们可以直接获取属性的值,getSize方法会被自动调用
Out[7]:
(150, 100)

静态方法 和 类成员方法

  • 静态方法在创建时会装入Staticmethod类型的对象中;
    类成员方法在创建时会装入Classmethod类型的对象中
  • 静态方法没有self对象,但却能被类本身调用(而不一定被实例调用)
    类方法在定义时用cls参数取代self参数
    类成员方法可以直接用类的具体对象来调用,此时cls参数是自动绑定到类的

这两种方法并不是很重要,因为通常可以使用函数或者绑定方法来代替

In [1]:
__metaclass__ = type
In [2]:
#手动包装和替换方法的技术
class Myclass:
    def smeth():    #没有self参数
        print 'This is a static method'
    smeth = staticmethod(smeth)   #手动把smeth方法包装成staticmethod类型并且同名替换
    
    def cmeth(cls):       #用cls代替self参数
        print 'This is a class method of', cls
    cmeth = classmethod(cmeth)       #与smeth类似
In [3]:
#新的装饰器(decorator)语法
class Myclass:
    @staticmethod       #在方法或函数的上方列出装饰器(@操作符)
    def smeth():
        print 'This is a static method'
    
    @classmethod
    def cmeth(cls):
        print 'This is a class method of', cls
#装饰器可以有多个,但是应用的顺序与指定的顺序相反    
In [4]:
Myclass.smeth()       #可以直接对类调用静态方法
This is a static method

In [5]:
Myclass.cmeth()       #也可以对类直接调用类方法
This is a class method of <class '__main__.Myclass'>

与特性有关的魔法方法

  • __getattribute__(self, name):当name特性被试图访问时自动被调用(只适用于新式类)
  • __getattr__(self, name):当name特性被试图访问且该特性不存在时自动被调用
    ¥区分__getattribute__和__getattr__¥
    前者只适用于新式类,后者通用
    前者无论指定的特性是否存在都会调用,后者只有在特性不存在时才会调用
    前者会截断所有特性(包括__dict__和hasattr、getattr对特性的访问),后者只会截断一般特性(不包括前面所述的特殊访问)
    所以,在使用前者时,要在方法中访问与self有关的特性,就要通过超类的__getattribute__方法,并借助super函数
  • __setattr__(self, name, value):当name特性被试图赋值时自动被调用,而且所赋的值也作为value被截断
  • __delattr__(self, name):当name特性被试图删除时自动被调用
  • __dict__:特性是储存在一个字典中的,通过这个方法可以返回这个字典

使用这些方法我们可以实现对象特性访问、赋值、删除的截断

In [2]:
#可以用这些方法来实现property函数中的Rectangle类
class Rectangle:
    def __init__(self):
        self.width = 0
        self.height = 0
    def __setattr__(self, name, value):         #如果赋值的特性不是name,这个方法也会被调用,两方面都要考虑,__getattr__同理
        if name == 'size':
            self.width, self.height = size
        else:
            self.__dict__[name] = value           #可以直接通过修改__dict__返回的字典直接修改特性
            #此处如果改成self.name = value的赋值操作,会导致__setattr__方法再次被调用并且陷入死循环,要注意!!!
    def __getattr__(self, name):      #这里不能使用__getattribute__,否则也会陷入死循环
        if name == 'size':
            return self.width, self.height        #因为width和height特性是存在的,所以访问时不会再次调用这个方法
        else:
            raise AttributeError
In [31]:
#如何在__getattribute__方法内访问self的特性
In [3]:
__metaclass__ = type         #__getattribute__方法只适用于新式类
In [18]:
class Object:
    def __getattribute__(self, name):
        return 'hahahhahahha'
In [32]:
class Person(Object):         #Person是Object的子类
    def __init__(self):
        self.name = 'Zk'
        self.age = 21
    def __getattribute__(self, name):
        return super(Object,self).__getattribute__(name)
        #通过调用超类的__getattribute__方法
        #而且这里的超类Object必须使用super函数
        #super函数的参数是超类Object而不是Person(这和继承超类的初始值super(Person, self).__init__()不同)
        #而且这个方法不受超类的__getattribute__方法返回值影响
In [29]:
a = Person()
In [30]:
a.name          #得到不是“hahahahahhahah”哦!
Out[30]:
'Zk'
In []: