上篇我们写到第九条内容,接下来我们继续。
十.抽象类
什么是抽象类:
与 java 一样,Python 也有抽象类的概念但是同样需要借助模块实现,抽象类是一个特殊的类,它的特殊之处在于只能被继承,不能被实例化。
为什么要有抽象类:
如果说类是从一堆对象中抽取相同的内容而来的,那么抽象类就是从一堆类中抽取相同的内容而来的,内容包括数据属性和函数属性。 比如我们有香蕉的类,有苹果的类,有桃子的类,从这些类抽取相同的内容就是水果这个抽象的类,你吃水果时,要么是吃一个具体的香蕉,要么是吃一个具体的桃子。。。。。。你永远无法吃到一个叫做水果的东西。从设计角度去看,如果类是从现实对象抽象而来的,那么抽象类就是基于类抽象而来的。从实现角度来看,抽象类与普通类的不同之处在于:抽象类中只能有抽象方法(没有实现功能),该类不能被实例化,只能被继承,且子类必须实现抽象方法。这一点与接口有点类似,但其实是不同的,即将揭晓答案
其实抽象类说直白了就是让你们的程序更加的规范化:
1 #!/usr/bin/env python
2 #_*_coding:utf-8_*_
3 #@author :yinzhengjie
4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/
5 #EMAIL:y1053419035@qq.com
6 #一切皆文件
7 import abc #利用abc模块实现抽象类
8
9 class All_file(metaclass=abc.ABCMeta):
10 all_type='file'
11 @abc.abstractmethod #定义抽象方法,无需实现功能,只要这个类中的方法中含有该装饰器“abc.abstractmethod”,所以继承该类的人都要重写该方法!
12 def read(self):
13 '子类必须定义读功能'
14 pass
15 @abc.abstractmethod #定义抽象方法,无需实现功能
16 def write(self):
17 '子类必须定义写功能'
18 pass
19 # class Txt(All_file):
20 # pass
21 #
22 # t1=Txt() #报错,子类没有定义抽象方法
23
24 class Txt(All_file): #子类继承抽象类,但是必须定义read和write方法
25 def read(self):
26 print('文本数据的读取方法')
27
28 def write(self):
29 print('文本数据的读取方法')
30
31 class Sata(All_file): #子类继承抽象类,但是必须定义read和write方法
32 def read(self):
33 print('硬盘数据的读取方法')
34
35 def write(self):
36 print('硬盘数据的读取方法')
37
38 class Process(All_file): #子类继承抽象类,但是必须定义read和write方法
39 def read(self):
40 print('进程数据的读取方法')
41
42 def write(self):
43 print('进程数据的读取方法')
44
45 wenbenwenjian=Txt()
46
47 yingpanwenjian=Sata()
48
49 jinchengwenjian=Process()
50
51 #这样大家都是被归一化了,也就是一切皆文件的思想
52 wenbenwenjian.read()
53 yingpanwenjian.write()
54 jinchengwenjian.read()
55
56 print(wenbenwenjian.all_type)
57 print(yingpanwenjian.all_type)
58 print(jinchengwenjian.all_type)
59
60
61 #以上代码执行结果如下:
62 文本数据的读取方法
63 硬盘数据的读取方法
64 进程数据的读取方法
65 file
66 file
67 file
- 抽象类与接口
抽象类的本质还是类,指的是一组类的相似性,包括数据属性(如 all_type )和函数属性(如 read、write ),而接口只强调函数属性的相似性。抽象类是一个介于类和接口直接的一个概念,同时具备类和接口的部分特性,可以用来实现归一化设计
十一.多态
多态性( polymorphisn )是允许你将父对象设置成为和一个或更多的他的子对象相等的技术,赋值之后,父对象就可以根据当前赋值给它的子对象的特性以不同的方式运作。简单的说,就是一句话:允许将子类类型的指针赋值给父类类型的指针。
那么,多态的作用是什么呢?我们知道,封装可以隐藏实现细节,使得代码模块化;继承可以扩展已存在的代码模块(类);它们的目的都是为了——代码重用。而多态则是为了实现另一个目的——接口重用!多态的作用,就是为了类在继承和派生的时候,保证使用“家谱”中任一类的实例的某一属性时的正确调用。
多态:我们可以这么理解,就是一种事物的不同形态如序列类型包含:字符串,列表,元组;
多态性:一个接口函数有多种实现方式。
1 #!/usr/bin/env python
2 #_*_coding:utf-8_*_
3 #@author :yinzhengjie
4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/
5 #EMAIL:y1053419035@qq.com
6 #序列内形的不同形态如下:分别有字符串,元组和列表,这就体现了同一种食物的不同形态,简称多态。
7 name = "尹正杰"
8 tuple_test = ("yinzhengjie","123")
9 list_test = ["尹正杰","25","175","北京","朝阳区"]
10
11
12 #多态性:一个入口函数有多种实现方式
13 def fun(obj): #"obj"这个参数就体现多态性。原理就是传递进来的对象都有相同(同名)的“__len__()”方法,只不过同一个方法会执行不同的功能而已。
14 print(obj.__len__()) #相当于len(obj)
15
16 fun(name)
17 fun(tuple_test)
18 fun(list_test)
19
20
21 #以上代码的执行结果如下:
22 3
23 2
24 5
1 #!/usr/bin/env python
2 #_*_coding:utf-8_*_
3 #@author :yinzhengjie
4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/
5 #EMAIL:y1053419035@qq.com
6 #多态如下:
7 class Animal:
8 def talk(self):
9 pass
10 class People(Animal):
11 def talk(self):
12 print("say hello")
13
14 class Pig(Animal):
15 def talk(self):
16 print("say heng····heng")
17
18 class Dog(Animal):
19 def talk(self):
20 print("say wang···wang")
21
22 class Cat(Animal):
23 def talk(self):
24 print("miao~miao~miao~")
25
26 p1 = People() #这就是一个实例,其本质上就是将“p1 = People()”传递给“self”
27 pig1 = Pig()
28 D1 = Dog()
29 c = Cat()
30
31 #多态性:
32 def fun(obj):
33 obj.talk()
34 fun(p1)
35 fun(pig1)
36 fun(D1)
37 fun(c)
38
39 #以上代码执行结果如下:
40 say hello
41 say heng····heng
42 say wang···wang
43 miao~miao~miao~
1 #!/usr/bin/env python
2 #_*_coding:utf-8_*_
3 #@author :yinzhengjie
4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/
5 #EMAIL:y1053419035@qq.com
6 #定义多态如下:
7 class Animal(object):
8 def __init__(self, name): # Constructor of the class
9 self.name = name
10
11 def talk(self): # Abstract method, defined by convention only
12 raise NotImplementedError("Subclass must implement abstract method")
13 class Cat(Animal):
14 def talk(self):
15 print('【%s】: 喵喵喵!' % self.name)
16 class Dog(Animal):
17 def talk(self):
18 print('【%s】: 汪!汪!汪!' % self.name)
19 #定义多态性:
20 def func(obj): # 一个接口,多种形态
21 obj.talk()
22 c1 = Cat('hello kitty')
23 d1 = Dog('犬夜叉')
24
25 func(c1)
26 func(d1)
27
28 #以上代码执行结果如下:
29 【hello kitty】: 喵喵喵!
30 【犬夜叉】: 汪!汪!汪!
Pyhon 很多语法都是支持多态的,比如 len(),sorted(), 你给 len 传字符串就返回字符串的长度,传列表就返回列表长度。
1 #!/usr/bin/env python
2 #_*_coding:utf-8_*_
3 #@author :yinzhengjie
4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/
5 #EMAIL:y1053419035@qq.com
6 '''
7 继承的作用:
8 1>.直接调用父类方法;
9 2>.继承父类方法并重构父类方法,先重构,在重构的方法里手动调用父类方法
10 3>.可以定义子类自己的方法;
11 4>.可以析构方法"__del__"
12 '''
13 class SchoolMember(object):
14 members = 0
15 test = 111111
16 print("in SchoolMember ")
17 def __init__(self,name,age,sex):
18 self.name = name
19 self.age = age
20 self.sex = sex
21 SchoolMember.members += 1 #注意着是调用的类变量,我们不能用"self.members"去自加哟!
22 print("初始化了一个新学校成员",self.name)
23 class Course(object): #定义一个类其实也可以不用谢初始化函数用,只写类变量也可以
24 test = 666666
25 print("in Course ")
26 Course_name = "python自动化"
27 period = "9m"
28 outtline = "test"
29 class Student(SchoolMember,Course): #继承了2个父类,继承顺序优先级由左往右依次递减
30 def __init__(self, name, age, sex, salary): # 初始化函数
31 SchoolMember.__init__(self, name, age, sex)
32 def pay_tuition(self, amount):
33 self.paid_tuition = amount #存在实例里,方便其他函数(方法)调用。
34 print("student %s has paid tution amoint %s" % (self.name, amount))
35 s = Student("yinzhengjie","25","M","pys11000")
36 print(s.Course_name,Course.outtline)
37 print(s.test)
38
39
40 #以上代码执行结果如下:
41 in SchoolMember
42 in Course
43 初始化了一个新学校成员 yinzhengjie
44 python自动化 test
45 111111
十二.静态方法和类方法
通常情况下,在类中定义的所有函数(注意了,这里说的就是所有,跟 self 啥的没关系,self 也只是一个再普通不过的参数而已)都是对象的绑定方法,对象在调用绑定方法时会自动将自己作为参数传递给方法的第一个参数。除此之外还有两种常见的方法:静态方法和类方法,二者是为类量身定制的,但是实例非要使用,也不会报错,后续将介绍。
1.静态方法
是一种普通函数,位于类定义的命名空间中,不会对任何实例类型进行操作,Python 为我们内置了函数 staticmethod 来把类中的函数定义成静态方法。静态方法既不属于类也不属于实例。与类只是属于名义上的归属关系
class Foo:
def spam(x,y,z): #类中的一个函数,千万不要懵逼,self和x啥的没有不同都是参数名
print(x,y,z)
spam=staticmethod(spam) #把spam函数做成静态方法
基于之前所学装饰器的知识,@staticmethod 等同于spam=staticmethod(spam),于是
class Foo:
@staticmethod #装饰器
def spam(x,y,z):
print(x,y,z)
使用演示
1 #!/usr/bin/env python
2 #_*_coding:utf-8_*_
3 #@author :yinzhengjie
4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/
5 #EMAIL:y1053419035@qq.com
6
7 class Foo:
8 @staticmethod #装饰器
9 def spam(x,y,z):
10 print(x,y,z)
11
12
13 print(type(Foo.spam)) #类型本质就是函数
14 Foo.spam(100,200,300) #调用函数应该有几个参数就传几个参数
15
16 f1=Foo()
17 f1.spam(6,6,6) #实例也可以使用,但通常静态方法都是给类用的,实例在使用时丧失了自动传值的机制
18
19
20
21 #以上代码执行结果如下:
22 <class 'function'>
23 100 200 300
24 6 6 6
** 使用展示二:**
1 #!/usr/bin/env python
2 #_*_coding:utf-8_*_
3 #@author :yinzhengjie
4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/
5 #EMAIL:y1053419035@qq.com
6
7 class food(object):
8 def __init__(self,name):
9 self.name = name
10 print("good boy!")
11 @staticmethod
12 def eat():
13 print("你吃了吗?")
14
15 test = food("rice")
16 test.eat()
17 food.eat() #不用实例化就可以直接调用静态方法,所以说静态方法既不属于类也不属于实例。与类只是属于名义上的归属关系,两者的唯一联系就是在调用静态方法(eat)的时候必须先调用该类(food).
18
19 #以上代码执行结果如下:
20 good boy!
21 你吃了吗?
22 你吃了吗?
应用场景:编写类时需要采用很多不同的方式来创建实例,而我们只有一个init函数,此时静态方法就派上用场了
#!/usr/bin/env python
#_*_coding:utf-8_*_
#@author :yinzhengjie
#blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/
#EMAIL:y1053419035@qq.com
import time
class Date:
def __init__(self,year,month,day):
self.year=year
self.month=month
self.day=day
@staticmethod
def now(): #用Date.now()的形式去产生实例,该实例用的是当前时间
t=time.localtime() #获取结构化的时间格式
return Date(t.tm_year,t.tm_mon,t.tm_mday) #新建实例并且返回
@staticmethod
def tomorrow():#用Date.tomorrow()的形式去产生实例,该实例用的是明天的时间
t=time.localtime(time.time()+86400)
return Date(t.tm_year,t.tm_mon,t.tm_mday)
a=Date('2014',7,18) #自己定义时间
b=Date.now() #采用当前时间
c=Date.tomorrow() #采用明天的时间
print(a.year,a.month,a.day)
print(b.year,b.month,b.day)
print(c.year,c.month,c.day)
#以上代码执行结果如下:
2014 7 18
2017 5 9
2017 5 10
2.类方法
类方法是给类用的,类在使用时会将类本身当做参数传给类方法的第一个参数,Python 为我们内置了函数 classmethod 来把类中的函数定义成类方法,类方法属于类不属于实例。因为类方法能访问类变量,但是无法访问实例变量。
使用展示一:
1 #!/usr/bin/env python
2 #_*_coding:utf-8_*_
3 #@author :yinzhengjie
4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/
5 #EMAIL:y1053419035@qq.com
6
7
8 class A:
9 x=1
10 @classmethod
11 def test(cls):
12 print(cls,cls.x)
13
14 class B(A):
15 x=100
16
17 B.test()
18 print(A)
19
20
21 #以上代码执行结果如下:
22 <class '__main__.B'> 100
23 <class '__main__.A'>
使用展示二:
1 #!/usr/bin/env python
2 #_*_coding:utf-8_*_
3 #@author :yinzhengjie
4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/
5 #EMAIL:y1053419035@qq.com
6
7 class Man(object):
8 name = "尹正杰" #定义一个类变量
9 def __init__(self,name):
10 self.name = name #实例变量
11 print("good boy!")
12 @classmethod #类方法
13 def talk(self):
14 print("%s is talking"% self.name) #类方法只能调用class中的方法或变量,但是无法调用某个方法下的实例变量。
15
16 @staticmethod #静态方法
17 def eat():
18 print("你吃了吗?")
19
20 a = Man("yinzhengjie")
21 a.talk()
22
23
24
25
26 #以上代码执行结果如下:
27 good boy!
28 尹正杰 is talking
应用场景:
1 #!/usr/bin/env python
2 #_*_coding:utf-8_*_
3 #@author :yinzhengjie
4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/
5 #EMAIL:y1053419035@qq.com
6
7 import time
8 class Date:
9 def __init__(self,year,month,day):
10 self.year=year
11 self.month=month
12 self.day=day
13 @staticmethod
14 def now():
15 t=time.localtime()
16 return Date(t.tm_year,t.tm_mon,t.tm_mday)
17
18 class EuroDate(Date):
19 def __str__(self):
20 return 'year:%s month:%s day:%s' %(self.year,self.month,self.day)
21
22 e=EuroDate.now()
23 print(e) #我们的意图是想触发EuroDate.__str__,但是结果为“<__main__.Date object at 0x029F38D0>”
24
25
26
27 #以上代码执行结果如下:
28 <__main__.Date object at 0x029838D0>
原因:
因为 e 就是用 Date 类产生的,所以根本不会触发 EuroDate.str, 解决方法就是用 classmethod
1 #!/usr/bin/env python
2 #_*_coding:utf-8_*_
3 #@author :yinzhengjie
4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/
5 #EMAIL:y1053419035@qq.com
6
7 import time
8 class Date:
9 def __init__(self,year,month,day):
10 self.year=year
11 self.month=month
12 self.day=day
13 # @staticmethod
14 # def now():
15 # t=time.localtime()
16 # return Date(t.tm_year,t.tm_mon,t.tm_mday)
17
18 @classmethod #改成类方法
19 def now(cls):
20 t=time.localtime()
21 return cls(t.tm_year,t.tm_mon,t.tm_mday) #哪个类来调用,即用哪个类cls来实例化
22
23 class EuroDate(Date):
24 def __str__(self):
25 return 'year:%s month:%s day:%s' %(self.year,self.month,self.day)
26
27 e=EuroDate.now()
28 print(e) #我们的意图是想触发EuroDate.__str__,此时e就是由EuroDate产生的,所以会如我们所愿
29
30 '''
31 注意:
32 静态方法和类方法虽然是给类准备的,但是如果实例去用,也是可以用的,只不过实例去调用的时候容易让人混淆,不知道你要干啥
33 '''
34 x=e.now() #通过实例e去调用类方法也一样可以使用,静态方法也一样
35 print(x)
36
37
38 #以上代码执行结果如下:
39 year:2017 month:5 day:9
40 year:2017 month:5 day:9
注意:
以上所述都是 Python 都是 class 语音上的一些特性,你可以去用它,但是不一样要一定去用它!那么如何面向对象开发程序呢?所以下面基本上是写一个程序你需要考虑的因素,弄明白了需求才去用以上的方法去解决问题,所以下面的才是干货!必看哟~
十三.面向对象的软件开发
很多人在学完了 Python 的 class 机制之后,遇到一个生产中的问题,还是会懵逼,这其实太正常了,因为任何程序的开发都是先设计后编程,Python 的 class 机制只不过是一种编程方式,如果你硬要拿着 class 去和你的问题死磕,变得更加懵逼都是分分钟的事,在以前,软件的开发相对简单,从任务的分析到编写程序,再到程序的调试,可以由一个人或一个小组去完成。但是随着软件规模的迅速增大,软件任意面临的问题十分复杂,需要考虑的因素太多,在一个软件中所产生的错误和隐藏的错误、未知的错误可能达到惊人的程度,这也不是在设计阶段就完全解决的。
所以软件的开发其实一整套规范,我们所学的只是其中的一小部分,一个完整的开发过程,需要明确每个阶段的任务,在保证一个阶段正确的前提下再进行下一个阶段的工作,称之为软件工程
面向对象的软件工程包括下面几个部:
1.面向对象分析(object oriented analysis ,OOA)
软件工程中的系统分析阶段,要求分析员和用户结合在一起,对用户的需求做出精确的分析和明确的表述,从大的方面解析软件系统应该做什么,而不是怎么去做。面向对象的分析要按照面向对象的概念和方法,在对任务的分析中,从客观存在的事物和事物之间的关系,贵南出有关的对象(对象的‘特征’和‘技能’)以及对象之间的联系,并将具有相同属性和行为的对象用一个类class来标识。
建立一个能反映这是工作情况的需求模型,此时的模型是粗略的。
2.面向对象设计(object oriented design,OOD)
根据面向对象分析阶段形成的需求模型,对每一部分分别进行具体的设计。
首先是类的设计,类的设计可能包含多个层次(利用继承与派生机制)。然后以这些类为基础提出程序设计的思路和方法,包括对算法的设计。
在设计阶段并不牵涉任何一门具体的计算机语言,而是用一种更通用的描述工具(如伪代码或流程图)来描述
3.面向对象编程(object oriented programming,OOP)
根据面向对象设计的结果,选择一种计算机语言把它写成程序,可以是 Python
4.面向对象测试(object oriented test,OOT)
在写好程序后交给用户使用前,必须对程序进行严格的测试,测试的目的是发现程序中的错误并修正它。
面向对的测试是用面向对象的方法进行测试,以类作为测试的基本单元。
5.面向对象维护(object oriendted soft maintenance,OOSM)
正如对任何产品都需要进行售后服务和维护一样,软件在使用时也会出现一些问题,或者软件商想改进软件的性能,这就需要修改程序。
由于使用了面向对象的方法开发程序,使用程序的维护比较容易。
因为对象的封装性,修改一个对象对其他的对象影响很小,利用面向对象的方法维护程序,大大提高了软件维护的效率,可扩展性高。
在面向对象方法中,最早发展的肯定是面向对象编程( OOP ),那时 OOA 和OOD 都还没有发展起来,因此程序设计者为了写出面向对象的程序,还必须深入到分析和设计领域,尤其是设计领域,那时的 OOP 实际上包含了现在的 OOD 和 OOP 两个阶段,这对程序设计者要求比较高,许多人感到很难掌握。
现在设计一个大的软件,是严格按照面向对象软件工程的5个阶段进行的,这个5个阶段的工作不是由一个人从头到尾完成的,而是由不同的人分别完成,这样OOP 阶段的任务就比较简单了。程序编写者只需要根据 OOD 提出的思路,用面向对象语言编写出程序既可。
在一个大型软件开发过程中,OOP 只是很小的一个部分。
对于全栈开发的你来说,这五个阶段都有了,对于简单的问题,不必严格按照这个5个阶段进行,往往由程序设计者按照面向对象的方法进行程序设计,包括类的设计和程序的设计
十四.小白容易犯的错误
1. 面向对象的程序设计看起来高大上,所以我在编程时就应该保证通篇class,这样写出的程序一定是好的程序(面向对象只适合那些可扩展性要求比较高的场景)
2. 很多人喜欢说面向对象三大特性(这是从哪传出来的,封装,多态,继承?漏洞太多太多,好吧暂且称为三大特性),那么我在基于面向对象编程时,我一定要让我定义的类中完整的包含这三种特性,这样写肯定是好的程序
好家伙,我说降龙十八掌有十八掌,那么你每次跟人干仗都要从第一掌打到第18掌这才显得你会了是么,我来一万个人你需要打10000*18掌对么,傻叉
3. 类有类属性,实例有实例属性,所以我们在定义 class 时一定要定义出那么几个类属性,想不到怎么办,那就使劲的想,定义的越多越牛逼
这就犯了一个严重的错误,程序越早面向对象,死的越早,为啥面向对象,因为我们要将数据与功能结合到一起,程序整体的结构都没有出来,或者说需要考虑的问题你都没有搞清楚个八九不离十,你就开始面向对象了,这就导致了,你在那里干想,自以为想通了,定义了一堆属性,结果后来又都用不到,或者想不通到底应该定义啥,那就一直想吧,想着想着就疯了。
你见过哪家公司要开发一个软件,上来就开始写,肯定是频繁的开会讨论计划,请看第八节
4. 既然这么麻烦,那么我彻底解脱了,我们不要用面向对象编程了,你啊,你有大才,你能成事啊,傻叉。
十五.python中关于OOP的常用术语
1.抽象/实现
抽象指对现实世界问题和实体的本质表现,行为和特征建模,建立一个相关的子集,可以用于 绘程序结构,从而实现这种模型。抽象不仅包括这种模型的数据属性,还定义了这些数据的接口。
对某种抽象的实现就是对此数据及与之相关接口的现实化(realization)。现实化这个过程对于客户 程序应当是透明而且无关的。
2.封装/接口
封装描述了对数据/信息进行隐藏的观念,它对数据属性提供接口和访问函数。通过任何客户端直接对数据的访问,无视接口,与封装性都是背道而驰的,除非程序员允许这些操作。作为实现的 一部分,客户端根本就不需要知道在封装之后,数据属性是如何组织的。在Python中,所有的类属性都是公开的,但名字可能被“混淆”了,以阻止未经授权的访问,但仅此而已,再没有其他预防措施了。这就需要在设计时,对数据提供相应的接口,以免客户程序通过不规范的操作来存取封装的数据属性。
注意:
封装绝不是等于“把不想让别人看到、以后可能修改的东西用private隐藏起来”真正的封装是,经过深入的思考,做出良好的抽象,给出“完整且最小”的接口,并使得内部细节可以对外透明(注意:对外透明的意思是,外部调用者可以顺利的得到自己想要的任何功能,完全意识不到内部细节的存在)
3.合成
合成扩充了对类的 述,使得多个不同的类合成为一个大的类,来解决现实问题。合成 述了 一个异常复杂的系统,比如一个类由其它类组成,更小的组件也可能是其它的类,数据属性及行为, 所有这些合在一起,彼此是“有一个”的关系。
4.派生/继承/继承结构
派生描述了子类衍生出新的特性,新类保留已存类类型中所有需要的数据和行为,但允许修改或者其它的自定义操作,都不会修改原类的定义。
继承描述了子类属性从祖先类继承这样一种方式
继承结构表示多“代”派生,可以述成一个“族谱”,连续的子类,与祖先类都有关系。
5.泛化/特化
基于继承
泛化表示所有子类与其父类及祖先类有一样的特点。
特化描述所有子类的自定义,也就是,什么属性让它与其祖先类不同。
6.多态与多态性
多态指的是同一种事物的多种状态:水这种事物有多种不同的状态:冰,水蒸气
多态性的概念指出了对象如何通过他们共同的属性和动作来操作及访问,而不需考虑他们具体的类。
冰,水蒸气,都继承于水,它们都有一个同名的方法就是变成云,但是冰.变云(),与水蒸气.变云()是截然不同的过程,虽然调用的方法都一样
7.自省/反射
自省也称作反射,这个性质展示了某对象是如何在运行期取得自身信息的。如果传一个对象给你,你可以查出它有什么能力,这是一项强大的特性。如果 Python 不支持某种形式的自省功能,dir 和 type 内建函数,将很难正常工作。还有那些特殊属性,像 dict, name 及 doc
8.类的特殊成员方法
1>. "doc"表示类的描述信息
1 #!/usr/bin/env python
2 #_*_coding:utf-8_*_
3 #@author :yinzhengjie
4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/
5 #EMAIL:y1053419035@qq.com
6
7
8 class Foo:
9 """ 未成年勿进,老司机要开车了"""
10 def func(self):
11 pass
12
13 print(Foo.__doc__)
14
15
16
17 #以上代码执行结果如下:
18 未成年勿进,老司机要开车了
2>. module 和 class
假设存在以下目录结构:
1 #!/usr/bin/env python
2 #_*_coding:utf-8_*_
3 #@author :yinzhengjie
4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/
5 #EMAIL:y1053419035@qq.com
6 class Name:
7 def __init__(self):
8 self.name = 'yinzhengjie'
1 #!/usr/bin/env python
2 #_*_coding:utf-8_*_
3 #@author :yinzhengjie
4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/
5 #EMAIL:y1053419035@qq.com
6
7
8 from lib.test import Name
9 '''
10 __module__ 表示当前操作的对象在那个模块.
11
12 __class__ 表示当前操作的对象的类是什么.
13 '''
14
15 obj = Name()
16 print(obj.__module__) # 输出模块
17 print(obj.__class__) # 输出类
18
19
20 #以上代码执行结果如下:
21 lib.test
22 <class 'lib.test.Name'>
1 #!/usr/bin/env python
2 #_*_coding:utf-8_*_
3 #@author :yinzhengjie
4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/
5 #EMAIL:y1053419035@qq.com
6
7 import importlib
8 '''
9 导入模块,我们可以花式玩,请往下看。
10 '''
11
12
13 #导入方法一:这是puthon解释器自己内部用的。
14 __import__('lib.test')
15
16 #导入方法二:与上面这局效果一样,官方建议用这个。
17 importlib.import_module('lib.test')
3>. init 构造方法,通过类创建对象时,自动触发执行。
4>. del析构方法,当对象在内存中被释放时,自动触发执行。
注:此方法一般无须定义,因为 Python 是一门高级语言,程序员在使用时无需关心内存的分配和释放,因为此工作都是交给 Python 解释器来执行,所以,析构函数的调用是由解释器在进行垃圾回收时自动触发执行的
5>. call 对象后面加括号,触发执行。
注:构造方法的执行是由创建对象触发的,即:对象 = 类名() ;而对于 call 方法的执行是由对象后加括号触发的,即:对象() 或者 类()()
1 #!/usr/bin/env python
2 #_*_coding:utf-8_*_
3 #@author :yinzhengjie
4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/
5 #EMAIL:y1053419035@qq.com
6 class Foo:
7 def __init__(self):
8 pass
9
10 def __call__(self, *args, **kwargs):
11 print("My name is yinzhengjie")
12
13 a = Foo() #对这个类进行实例化。
14 a() #调用该实例。
15
16
17
18 #以上代码执行结果如下:
19 My name is yinzhengjie
6>. dict 查看类或对象中的所有成员
1 #!/usr/bin/env python
2 #_*_coding:utf-8_*_
3 #@author :yinzhengjie
4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/
5 #EMAIL:y1053419035@qq.com
6 class Province:
7 country = 'China'
8
9 def __init__(self, name, count):
10 self.name = name
11 self.count = count
12
13 def func(self, *args, **kwargs):
14 print('func')
15
16
17 # 获取类的成员,即:静态字段、方法、
18 print(Province.__dict__)
19
20 obj1 = Province('陕西', "1924km")
21 print(obj1.__dict__) # 获取 对象obj1 的成员
22
23 obj2 = Province('河北', "365km")
24 print(obj2.__dict__) # 获取 对象obj2 的成员
25
26
27
28 #以上代码执行结果如下:
7>. str 如果一个类中定义了str方法,那么在打印 对象 时,默认输出该方法的返回值。
1 #!/usr/bin/env python
2 #_*_coding:utf-8_*_
3 #@author :yinzhengjie
4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/
5 #EMAIL:y1053419035@qq.com
6 class Foo:
7 def __str__(self):
8 return 'yinzhengjie is good boy !'
9
10
11 obj = Foo()
12 print(obj)
13
14
15
16 #以上代码执行结果如下:
17 yinzhengjie is good boy !
8>. getitem、setitem、delitem
用于索引操作,如字典。以上分别表示获取、设置、删除数据
1 #!/usr/bin/env python
2 #_*_coding:utf-8_*_
3 #@author :yinzhengjie
4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/
5 #EMAIL:y1053419035@qq.com
6 class Foo(object):
7 def __getitem__(self, key):
8 print('__getitem__', key)
9
10 def __setitem__(self, key, value):
11 print('__setitem__', key, value)
12
13 def __delitem__(self, key):
14 print('__delitem__', key)
15
16
17 obj = Foo()
18
19 result = obj['Boy'] # 自动触发执行 __getitem__
20 obj['k2'] = 'Yinzhengjie' # 自动触发执行 __setitem__
21 del obj['123']
22
23
24 #以上代码执行结果如下:
25 __getitem__ Boy
26 __setitem__ k2 Yinzhengjie
27 __delitem__ 123
9>. new 和 metaclass
1 #!/usr/bin/env python
2 #_*_coding:utf-8_*_
3 #@author :yinzhengjie
4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/
5 #EMAIL:y1053419035@qq.com
6 class Foo(object):
7 def __init__(self, name):
8 self.name = name
9 f = Foo("yinzhengjie")
10 '''
11 注意:
12 上述代码中,obj 是通过 Foo 类实例化的对象,其实,不仅 obj 是一个对象,Foo类本身也是一个对象,因为在Python
13 中一切事物都是对象。如果按照一切事物都是对象的理论:obj对象是通过执行Foo类的构造方法创建,那么Foo类对象应该也是
14 通过执行某个类的 构造方法 创建。
15 '''
16 print(type(f))
17 print(type(Foo))
18
19
20 以上代码执行结果如下:
21 <class '__main__.Foo'> #输出:<class '__main__.Foo'> 表示,obj 对象由Foo类创建。
22 <class 'type'> # 输出:<type 'type'> 表示,Foo类对象由 type 类创建。
所以,f 对象是 Foo 类的一个实例,Foo 类对象是 type 类的一个实例,即:Foo 类对象 是通过 type 类的构造方法创建。
1 #!/usr/bin/env python
2 #_*_coding:utf-8_*_
3 #@author :yinzhengjie
4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/
5 #EMAIL:y1053419035@qq.com
6
7 #那么,创建类就可以有两种方式:
8 #a).普通方式
9 class Foo(object):
10 def func(self):
11 print('hello Yinzhengjie')
12 print(Foo)
13 print(type(Foo))
14 print("*"*50,"我是分割线","*"*50)
15 #b).特殊方式
16 def func(self,name):
17 print('hello',name)
18
19 f = type('Test', (object,), {'func': func})
20 # type第一个参数:类名
21 # type第二个参数:当前类的基类,也就是第一个参数的类名的父类。
22 # type第三个参数:类的成员(将其他函数封装进去)
23 print(f)
24 print(type(f))
25 print(dir(f)) #查看“f”类有哪些可用的方法
26 f_obj = f()
27 f_obj.func("尹正杰")
28
29
30
31
32 #以上代码执行结果如下:
33 <class '__main__.Foo'>
34 <class 'type'>
35 ************************************************** 我是分割线 **************************************************
36 <class '__main__.Test'>
37 <class 'type'>
38 ['__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__', 'func']
39 hello 尹正杰
1 #!/usr/bin/env python
2 #_*_coding:utf-8_*_
3 #@author :yinzhengjie
4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/
5 #EMAIL:y1053419035@qq.com
6
7 def func(self):
8 print("hello %s"%self.name)
9
10 def __init__(self,name,age):
11 self.name = name
12 self.age = age
13 Foo = type('Foo',(object,),{'func':func,'__init__':__init__})
14
15 f = Foo("Yinzhengjie",25)
16 f.func()
17
18
19 #以上代码执行结果如下:
20 hello Yinzhengjie
So ,要记住类 是由 type 类实例化产生
那么问题来了,类默认是由 type 类实例化产生,type类中如何实现的创建类?类又是如何创建对象?
答:类中有一个属性 metaclass,其用来表示该类由 谁 来实例化创建,所以,我们可以为 metaclass 设置一个type类的派生类,从而查看 类 创建的过程。
1 #!/usr/bin/env python
2 #_*_coding:utf-8_*_
3 #@author :yinzhengjie
4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/
5 #EMAIL:y1053419035@qq.com
6 class MyType(type):
7 def __init__(self,*args,**kwargs):
8
9 print("Mytype __init__",*args,**kwargs)
10
11 def __call__(self, *args, **kwargs):
12 print("Mytype __call__", *args, **kwargs)
13 obj = self.__new__(self)
14 print("obj ",obj,*args, **kwargs)
15 print(self)
16 self.__init__(obj,*args, **kwargs)
17 return obj
18
19 def __new__(cls, *args, **kwargs):
20 print("Mytype __new__",*args,**kwargs)
21 return type.__new__(cls, *args, **kwargs)
22
23 print("*"*50,"我是分割线","*"*50)
24 class Foo(object,metaclass=MyType):
25
26
27 def __init__(self,name):
28 self.name = name
29
30 print("Foo __init__")
31
32 def __new__(cls, *args, **kwargs):
33 print("Foo __new__",cls, *args, **kwargs)
34 return object.__new__(cls)
35
36 f = Foo("Yinzhengjie")
37 print("对象是:",f)
38 print("尹正杰",f.name)
39
40
41 以上代码执行结果如下:
42 ************************************************** 我是分割线 **************************************************
43 Mytype __new__ Foo (<class 'object'>,) {'__init__': <function Foo.__init__ at 0x006C0078>, '__new__': <function Foo.__new__ at 0x006C00C0>, '__qualname__': 'Foo', '__module__': '__main__'}
44 Mytype __init__ Foo (<class 'object'>,) {'__init__': <function Foo.__init__ at 0x006C0078>, '__new__': <function Foo.__new__ at 0x006C00C0>, '__qualname__': 'Foo', '__module__': '__main__'}
45 Mytype __call__ Yinzhengjie
46 Foo __new__ <class '__main__.Foo'>
47 obj <__main__.Foo object at 0x006BBC50> Yinzhengjie
48 <class '__main__.Foo'>
49 Foo __init__
50 对象是: <__main__.Foo object at 0x006BBC50>
51 尹正杰 Yinzhengjie
类的生成 调用 顺序依次是 new --> init --> call
metaclass 详解文章:
http://stackoverflow.com/questions/100003/what-is-a-metaclass-in-python
得票最高那个答案写的非常好