封装
我们可以给一个实例绑定很多属性,如果有些属性不希望被外部访问到怎么办?
Python
对属性权限的控制是通过属性名来实现的,如果一个属性由双下划线开头(__)
,该属性就无法被外部访问。看例子:
class Person(object):
def __init__(self, name):
self.name = name
self._title = 'Mr'
self.__job = 'Student'
p = Person('Bob')
print p.name
# => Bob
print p._title
# => Mr
print p.__job
# => Error
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'Person' object has no attribute '__job'
可见,只有以双下划线开头的
"__job"
不能直接被外部访问。
但是,如果一个属性以
"__xxx__"
的形式定义,那它又可以被外部访问了,以"__xxx__"
定义的属性在Python的类中被称为特殊属性
,有很多预定义的特殊属性可以使用,通常我们不要把普通属性用"__xxx__"
定义。
以单下划线开头的属性
"_xxx"
虽然也可以被外部访问,但是,按照习惯,他们不应该被外部访问。
python
的类没有访问权限的问题,也就是说所有的变量都是可访问的。实际上python
有私有的机制,就是在属性前加__
,但是这种私有机制实际上也是伪私有
,因为它其实是用一个别名来保存这个属性。例如在类A中的self.__a = 4
, 实际上__a
被修改成了_A__a
保持在类中了。
示例
请给
Person
类的__init__
方法中添加name
和score
参数,并把score
绑定到__score
属性上,看看外部是否能访问到。
以双下划线开头的属性无法被外部访问,"__xxx__"
除外。
class Person(object):
def __init__(self, name, score):
self.name = name
self.__score = score
p = Person('Bob', 59)
print p.name
print p.__score
继承
如果已经定义了Person
类,需要定义新的Student
和Teacher
类时,可以直接从Person
类继承:
class Person(object):
def __init__(self, name, gender):
self.name = name
self.gender = gender
定义Student
类时,只需要把额外的属性加上,例如score
:
class Student(Person):
def __init__(self, name, gender, score):
super(Student, self).__init__(name, gender)
self.score = score
一定要用 super(Student, self).__init__(name, gender)
去初始化父类,否则,继承自 Person
的 Student
将没有 name
和 gender
。
函数super(Student, self)
将返回当前类继承的父类,即 Person
,然后调用__init__()
方法,注意self
参数已在super()
中传入,在__init__()
中将隐式传递,不需要写出(也不能写)。
继承的子类:
1、会继承父类的属性和方法;
2、也可以自己定义,覆盖父类的属性和方法
用super()
调用父类的方法:
class A(object):
def method(self.arg):
pass
class B(A):
def method(self, arg):
super(B, self).method()
用类名调用父类的方法:
class A(object):
def method(self.arg):
pass
class B(A):
def method(self, arg):
A.method(arg)
类型判断
isinstance
# 判断类型issubclass
# 判断是否是子类
class Programer(object):
hobby = 'Play Computer'
def __init__(self, name,age,weight):
self.name = name
self._age = age
self.__weight = weight
@classmethod
def get_hobby(cls):
return cls.hobby
@property
def get_weight(self):
return self.__weight
def self_introduction(self):
print(('My Name is %s\nI am %s years old\n') % (self.name,self._age))
class BackendProgramer(Programer):
def __init__(self,name,age,weight,language):
super(BackendProgramer,self).__init__(name,age,weight)
self.language = language # 定义language这个属性
if __name__ == '__main__':
programer = BackendProgramer('Albert',25,80,'Python')
print(dir(programer))
print(programer.__dict__)
print(type(programer))
print(isinstance(programer,Programer)) # 判断是否是Programer这个类
运行结果:
['_Programer__weight', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_age', 'get_hobby', 'get_weight', 'hobby', 'language', 'name', 'self_introduction']
{'_Programer__weight': 80, '_age': 25, 'language': 'Python', 'name': 'Albert'}
<class '__main__.BackendProgramer'>
True
多重继承
除了从一个父类继承外,Python允许从多个父类继承,称为多重继承。
多重继承的继承链就不是一棵树了,它像这样:
class A(object):
def __init__(self, a):
print 'init A...'
self.a = a
class B(A):
def __init__(self, a):
super(B, self).__init__(a)
print 'init B...'
class C(A):
def __init__(self, a):
super(C, self).__init__(a)
print 'init C...'
class D(B, C):
def __init__(self, a):
super(D, self).__init__(a)
print 'init D...'
像这样,D 同时继承自 B 和 C,也就是 D 拥有了 A、B、C 的全部功能。多重继承通过 super()调用__init__()方法时,A 虽然被继承了两次,但__init__()只调用一次:
>>> d = D('d')
init A...
init C...
init B...
init D...
多重继承的目的是从两种继承树中分别选择并继承出子类,以便组合功能使用。
举个例子,Python的网络服务器有TCPServer、UDPServer、UnixStreamServer、UnixDatagramServer,而服务器运行模式有 多进程ForkingMixin 和 多线程ThreadingMixin两种。
要创建多进程模式的 TCPServer:
class MyTCPServer(TCPServer, ForkingMixin)
pass
要创建多线程模式的 UDPServer:
class MyUDPServer(UDPServer, ThreadingMixin):
pass
如果没有多重继承,要实现上述所有可能的组合需要 4x2=8 个子类。
示例
+-Person
+- Student
+- Teacher
是一类继承树;
+- SkillMixin
+- BasketballMixin
+- FootballMixin
是一类继承树。
通过多重继承,请定义“会打篮球的学生”和“会踢足球的老师”。
多重继承需要从两个或更多的类派生。
参考代码:
class Person(object):
pass
class Student(Person):
pass
class Teacher(Person):
pass
class SkillMixin(object):
pass
class BasketballMixin(SkillMixin):
def skill(self):
return 'basketball'
class FootballMixin(SkillMixin):
def skill(self):
return 'football'
class BStudent(Student, BasketballMixin):
pass
class FTeacher(Teacher, FootballMixin):
pass
s = BStudent()
print s.skill()
t = FTeacher()
print t.skill()
多态
类具有继承关系,并且子类类型可以向上转型看做父类类型,如果我们从 Person 派生出 Student和Teacher ,并都写了一个 whoAmI() 方法:
class Person(object):
def __init__(self, name, gender):
self.name = name
self.gender = gender
def whoAmI(self):
return 'I am a Person, my name is %s' % self.name
class Student(Person):
def __init__(self, name, gender, score):
super(Student, self).__init__(name, gender)
self.score = score
def whoAmI(self):
return 'I am a Student, my name is %s' % self.name
class Teacher(Person):
def __init__(self, name, gender, course):
super(Teacher, self).__init__(name, gender)
self.course = course
def whoAmI(self):
return 'I am a Teacher, my name is %s' % self.name
在一个函数中,如果我们接收一个变量 x,则无论该 x 是 Person、Student还是 Teacher,都可以正确打印出结果:
def who_am_i(x):
print x.whoAmI()
p = Person('Tim', 'Male')
s = Student('Bob', 'Male', 88)
t = Teacher('Alice', 'Female', 'English')
who_am_i(p)
who_am_i(s)
who_am_i(t)
运行结果:
I am a Person, my name is Tim
I am a Student, my name is Bob
I am a Teacher, my name is Alice
这种行为称为多态。也就是说,方法调用将作用在 x 的实际类型上。s 是Student类型,它实际上拥有自己的 whoAmI()方法以及从 Person继承的 whoAmI方法,但调用 s.whoAmI()总是先查找它自身的定义,如果没有定义,则顺着继承链向上查找,直到在某个父类中找到为止。
由于Python是动态语言,所以,传递给函数 who_am_i(x)的参数 x 不一定是 Person 或 Person 的子类型。任何数据类型的实例都可以,只要它有一个whoAmI()的方法即可:
class Book(object):
def whoAmI(self):
return 'I am a book'
这是动态语言和静态语言(例如Java)最大的差别之一。动态语言调用实例方法,不检查类型,只要方法存在,参数正确,就可以调用。
多态的要素:继承和方法重写
class Programer(object):
hobby = 'Play Computer'
def __init__(self, name,age,weight):
self.name = name
self._age = age
self.__weight = weight
@classmethod
def get_hobby(cls):
return cls.hobby
@property
def get_weight(self):
return self.__weight
def self_introduction(self):
print(('My Name is %s\nI am %s years old\n') % (self.name,self._age))
class BackendProgramer(Programer):
def __init__(self,name,age,weight,language):
super(BackendProgramer,self).__init__(name,age,weight)
self.language = language
# 重写父类的 self_introduction方法
def self_introduction(self):
print(('My Name is %s\nMy favorite language is %s\n') % (self.name,self.language))
# 判断传进来的参数是否属于Programer对象,如果是,直接调用对象的self_introduction()方法
def introduce(programer):
if isinstance(programer,Programer):
programer.self_introduction()
if __name__ == '__main__':
# 将对象实例化
programer = Programer('Albert',25,80)
backend_programer = BackendProgramer('Tim',30,70,'Python')
introduce(programer)
introduce(backend_programer)
运行结果:
My Name is Albert
I am 25 years old
My Name is Tim
My favorite language is Python
多态优点:
添加功能比较简单,只要判断是否属于这个父类,而不需要判断传进来的是哪个子类,直击调用这个方法。
示例
Python提供了open()函数来打开一个磁盘文件,并返回 File 对象。File对象有一个read()方法可以读取文件内容:
例如,从文件读取内容并解析为JSON结果:
import json
f = open('/path/to/file.json', 'r')
print json.load(f)
由于Python的动态特性,json.load()并不一定要从一个File对象读取内容。任何对象,只要有read()方法,就称为File-like Object,都可以传给json.load()。
请尝试编写一个File-like Object,把一个字符串 r‘["Tim", "Bob", "Alice"]‘包装成 File-like Object 并由 json.load() 解析。
只要为Students类加上 read()方法,就变成了一个File-like Object。
import json
class Students(object):
def read(self):
return r'["Tim", "Bob", "Alice"]'
s = Students()
print json.load(s)