实例属性&类属性

类是模板,而实例则是根据类创建的对象。实例属性是绑定在一个实例上的,各自独立,不会影响到其他实例,但如果类上绑定一个属性,则所有实例都可以访问类的属性,并且所有实例访问的类的属性都是同一个,如下示例:

class Person(object):#类属性address = 'earth'def __init__(self, name, gender, age):#实例属性self.name = name
        self.gender = gender
        self.age = age

p1 = Person('zhangsan', 'male', 20)
print(p1.name)
print(p1.gender)
print(p1.age)

print(Person.address)
print(p1.address)

print('********给实例设置address属性********')

p1.address = 'China'print(Person.address)
print(p1.address)复制代码

执行该代码结果如下:

zhangsan
male
20
earth
earth
********给实例设置address属性********
earth
China复制代码

如上代码可知,当在实例上给类属性赋值时,实际上是给这个实例绑定了同名的属性而已,并不会影响类属性和其他实例。由此可见使用实例访问一个属性时优先查找实例上是否有该属性,如果没有再去类上查找(当实例属性和类属性重名时,实例属性优先级高)。

实例方法&类方法&静态方法

class Demo(object):def __init__(self):super(Demo, self).__init__()#实例方法有隐含的self参数,代表实例对象def instanceMethod(self):return 'instance method'#用@classmethod装饰器来定义类方法,类方法有隐含的cls参数,代表类本身    @classmethoddef classMethod(cls):return 'class method'#用@staticmethod装饰器来定义静态方法,静态方法没有隐含的self或cls参数    @staticmethoddef staticMethod():return 'static method'demo = Demo()#实例方法只能用实例访问print(demo.instanceMethod())#类方法和静态方法既可以用类访问,可以用实例访问#类方法和静态方法的区别在于隐含参数的传递上print(Demo.classMethod())
print(demo.classMethod())
print(Demo.staticMethod())
print(demo.staticMethod())复制代码

类成员的访问控制

Python中类的成员默认都是公开的(public),在Python中没有类似public、private等关键词来修饰类的成员。在Python中定义私有成员只需要在变量名或函数名前加上两个下划线,那么这个函数或变量就变成私有的了。在外部使用原来的私有成员的名字时,会提示找不到。但这种方式只是一种约定的方式,如果想在外部调用,还是可以调用的,具体看如下代码:

#!/usr/bin/env python#-*- coding:utf-8 -*-class Demo(object):"""docstring for Demo"""def __init__(self):super(Demo, self).__init__()
        self.__message = 'Hello World'def __getMessage(self):return self.__messageif __name__ == '__main__':
    demo = Demo()#直接调用会报错:AttributeError: 'Demo' object has no attribute '__getMessage'#print(demo.__getMessage())#我们来看一下demo模块都有哪些成员print(dir(demo))"""
    可以看到原来Python使用一种name mangling(名称修饰)技术,将__membername替换成_classname__membername,
    ['_Demo__getMessage', '_Demo__message', ...]
    """#我们使用修饰后的名字就可以成功调用print(demo._Demo__message)
    print(demo._Demo__getMessage())复制代码

所以Python的私有成员并不是真正意义上的私有,在类外部也可以调用。

魔术方法(Magic Method)

Python的魔术方法有如下几个特点:

  • 定义在class中
  • 不需要直接调用
  • Python的某些函数或操作符会调用对应的特殊方法

我们来看一个示例,如果需要把一个类的实例变为str,就需要实现特殊方法__str__(),如下所示:

class Person(object):def __init__(self, name, gender):self.name = name
        self.gender = genderdef __str__(self):return 'person: %s, %s' % (self.name, self.gender)

p = Person('zhangsan', 'male')

print(p)#输出 person: zhangsan, male复制代码

还有很多其他的魔术函数,如果要一个实例表现得像一个list,可以用len()函数获取个数,需要实现__len__()方法。如果要把一个实例变为一个可调用的对象,需要实现__call__()方法等等。

get/set方法

使用 get/set 方法来封装对一个属性的访问在很多面向对象编程的语言中都很常见,如下所示:

class Student(object):def __init__(self, name, score):self.name = name
        self.__score = scoredef get_score(self):return self.__scoredef set_score(self, score):self.__score = score

s = Student('zhangsan', 90)
s.set_score(100)
print(s.get_score())# 输出100复制代码

但这样写起来有些麻烦,Python里提供了@property装饰器,可以把方法“装饰”成属性调用。如下所示:

class Student(object):def __init__(self, name, score):self.name = name
        self.__score = score    @propertydef score(self):return self.__score    @score.setterdef score(self, score):self.__score = score

s = Student('zhangsan', 90)
s.score = 100print(s.score)# 输出100复制代码

注意: 第一个score(self)是get方法,用@property装饰,第二个score(self, score)是set方法,用@score.setter装饰,@score.setter是前一个@property装饰后的副产品。这样就可以像使用属性一样设置score了。