Python培训
400-996-5531
如果你想开发一款游戏,想要实现角色的混搭,这个时候“面向对象过程”就能轻松的解决这个问题。
一.编程范式
编程是程序员用特定的语法+数据结构+算法组成的代码来告诉计算机如何执行任务的过程 。
一个程序是程序员为了得到一个任务结果而编写的一组指令的集合,正所谓条条大路通罗马,实现一个任务的方式有很多种不同的方式, 对这些不同的编程方式的特点进行归纳总结得出来的编程方式类别,即为编程范式。 不同的编程范式本质上代表对各种类型的任务采取的不同的解决问题的思路, 大多数语言只支持一种编程范式(比如说:java),当然也有些语言可以同时支持多种编程范式( Python )。 两种最重要的编程范式分别是面向过程编程和面向对象编程。
二.面向过程编程( Procedural Programming )
Procedural programming uses a list of instructions to tell the computer what to do step-by-step.
面向过程编程依赖 - 你猜到了- procedures,一个 procedure 包含一组要被进行计算的步骤, 面向过程又被称为 top-down languages, 就是程序从上到下一步步执行,一步步从上到下,从头到尾的解决问题 。基本设计思路就是程序一开始是要着手解决一个大的问题,然后把一个大问题分解成很多个小问题或子过程,这些子过程再执行的过程再继续分解直到小问题足够简单到可以在一个小步骤范围内解决。
举个典型的面向过程的例子, 数据库备份, 分三步,连接数据库,备份数据库,测试备份文件可用性。
三.面向对象编程
OOP 编程是利用“类”和“对象”来创建各种模型来实现对真实世界的描述,使用面向对象编程的原因一方面是因为它可以使程序的维护和扩展变得更简单,并且可以大大提高程序开发效率 ,另外,基于面向对象的程序可以使它人更加容易理解你的代码逻辑,从而使团队开发变得更从容。
面向对象的几个核心特性如下
1>.Class 类:一个类即是对一类拥有相同属性的对象的抽象、蓝图、原型。在类中定义了这些对象的都具备的属性( variables ( data ))、共同的方法(其实类就是一堆函数组成的);
2>.Object 对象:一个对象即是一个类的实例化(就好像是 zabbix 模板被监控的主机引用的过程)后实例,一个类必须经过实例化后方可在程序中调用,一个类可以实例化多个对象,每个对象亦可以有不同的属性,就像人类是指所有人,每个人是指具体的对象,人与人之前有共性,亦有不同;
3>.Encapsulation 封装:在类中对数据的赋值、内部调用对外部用户是透明的,这使类变成了一个胶囊或容器,里面包含着类的数据和方法;
4>.Inheritance 继承:一个类可以派生出子类,在这个父类里定义的属性、方法自动被子类继承;
5>.Polymorphism 多态:多态是面向对象的重要特性,简单点说:“一个接口,多种实现”,指一个基类中派生出了不同的子类,且每个子类在继承了同样的方法名的同时又对父类的方法做了不同的实现,这就是同一种事物表现出的多种形态。
编程其实就是一个将具体世界进行抽象化的过程,多态就是抽象化的一种体现,把一系列具体事物的共同点抽象出来, 再通过这个抽象的事物, 与不同的具体事物进行对话。
对不同类的对象发出相同的消息将会有不同的行为。比如,你的老板让所有员工在九点钟开始工作, 他只要在九点钟的时候说:“开始工作”即可,而不需要对销售人员说:“开始销售工作”,对技术人员说:“开始技术工作”, 因为“员工”是一个抽象的事物, 只要是员工就可以开始工作,他知道这一点就行了。至于每个员工,当然会各司其职,做各自的工作。多态允许将子类的对象当作父类的对象使用,某父类型的引用指向其子类型的对象,调用的方法是该子类型的方法。这里引用和调用方法的代码编译前就已经决定了,而引用所指向的对象可以在运行期间动态绑定
四.面向对象编程(Object-Oriented Programming )介绍
无论用什么形式来编程,我们都要明确记住以下原则:
1>.写重复代码是非常不好的低级行为;
2>.你写的代码需要经常变更;
其实 OOP 编程的主要作用也是使你的代码修改和扩展变的更容易,那么小白要问了,既然函数都能实现这个需求了,还要 OOP 干毛线用呢? 呵呵,说这话就像,古时候,人们打仗杀人都用刀,后来出来了枪,它的主要功能跟刀一样,也是杀人,然后小白就问,既然刀能杀人了,那还要枪干毛线,哈哈,显而易见,因为枪能更好更快更容易的杀人。函数编程与 OOP 的主要区别就是 OOP 可以使程序更加容易扩展和易更改。好了 ,我就不废话了,让我们开始写我们的第一个类吧:
最简单的类案列解析
五.面向对象的特性
1.什么是特性 property(是用来提供接口的一种方式)
property 是一种特殊的属性,访问它时会执行函数然后返回值
2 .为什么要用 property
将一个类的函数定义成特性以后,对象再去使用的时候 obj.name , 根本无法察觉自己的 name 是执行了一个函数然后计算出来的,这种特性的使用方式遵循了统一访问的原则,即它是将一个方法变成静态属性。
除此之外,看下
1 '''
2 ps:面向对象的封装有三种方式:
3 【public】
4 这种其实就是不封装,是对外公开的
5 【protected】
6 这种封装方式对外不公开,但对朋友(friend)或者子类(形象的说法是“儿子”,但我不知道为什么大家 不说“女儿”,就像“parent”本来是“父母”的意思,但中文都是叫“父类”)公开
7 【private】
8 这种封装对谁都不公开
9 '''
Python 并没有在语法上把它们三个内建到自己的 class 机制中,在 C++ 里一般会将所有的所有的数据都设置为私有的,然后提供 set 和 get 方法(接口)去设置和获取,在 Python 中通过 property 方法可以实现
使用展示一:
1 #!/usr/bin/env python
2 #_*_coding:utf-8_*_
3 #@author :yinzhengjie
4 #blog:#/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 Man(object):
7 name = "ddddd"
8 def __init__(self ,name):
9 self.name = name
10 @classmethod
11 def talk(self):
12 print("%s is talking" % self.name)
13 @property #把eat这个方法变成静态属性
14 def eat(self):
15 print("%s is eating"% self.name)
16 @eat.setter #赋值,
17 def eat(self,num):
18 print("changing status", num)
19 @eat.deleter #删除,
20 def eat(self):
21 print("deleting....eat")
22 d = Man("Yinzhengjie")
23 d.eat #触发 @property
24 d.eat = 1000 #触发 @eat.setter
25 del d.eat #触发 @eat.deleter
26
27
28 #以上代码执行结果如下:
29 Yinzhengjie is eating
30 changing status 1000
31 deleting....eat
使用展示二:
1 #!/usr/bin/env python
2 #_*_coding:utf-8_*_
3 #@author :yinzhengjie
4 #blog:#/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,val):
8 self.__NAME=val #将所有的数据属性都隐藏起来
9
10 @property
11 def name(self):
12 return self.__NAME #obj.name访问的是self.__NAME(这也是真实值的存放位置)
13
14 @name.setter
15 def name(self,value):
16 if not isinstance(value,str): #在设定值之前进行类型检查
17 raise TypeError('%s must be str' %value)
18 self.__NAME=value #通过类型检查后,将值value存放到真实的位置self.__NAME
19
20 @name.deleter
21 def name(self):
22 raise TypeError('Can not delete')
23
24 f=Foo('yinzhengjie')
25 print(f.name)
26 # f.name=10 #抛出异常'TypeError: 10 must be str'
27 # del f.name #抛出异常'TypeError: Can not delete'
28
29 #以上代码执行结果如下:
30 yinzhengjie
应用场景:
1 #!/usr/bin/env python
2 #_*_coding:utf-8_*_
3 #@author :yinzhengjie
4 #blog:#/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 Flight(object):
9 def __init__(self, name):
10 self.flight_name = name
11
12 def checking_status(self):
13 print("checking flight %s status " % self.flight_name)
14 return 3
15
16 @property
17 def flight_status(self):
18 status = self.checking_status()
19 if status == 0:
20 print("flight got canceled...")
21 elif status == 1:
22 print("flight is arrived...")
23 elif status == 2:
24 print("flight has departured already...")
25 else:
26 print("cannot confirm the flight status...,please check later")
27
28 @flight_status.setter # 修改
29 def flight_status(self, status):
30 status_dic = {
31 0: "canceled",
32 1: "arrived",
33 2: "departured"
34 }
35 print("\033[31;1mHas changed the flight status to \033[0m", status_dic.get(status))
36
37 @flight_status.deleter # 删除
38 def flight_status(self):
39 print("status got removed...")
40
41
42 f = Flight("CA980")
43 f.flight_status
44 f.flight_status = 2 # 触发@flight_status.setter
45 del f.flight_status # 触发@flight_status.deleter
46
47
48
49 #以上代码执行结果如下:
50 checking flight CA980 status
51 cannot confirm the flight status...,please check later
52 Has changed the flight status to departured
53 status got removed...
六.封装
封装,其实就是使用构造方法将内容封装到某个具体对象中,然后通过对象直接或者self间接获取被封装的内容
封装最好理解了。封装是面向对象的特征之一,是对象和类概念的主要特性。
封装,也就是把客观事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的类或者对象操作,对不可信的进行信息隐藏。
学习封装前要掌握的知识点:
1>.类变量:既可以作为默认公有属性也可以全局修改或增加新属性。
类变量的应用
2>.实例变量(成员属性):其特性和类变量相反,仅仅属于你自己的实例变量。一般构造函数的实例变量都是实例变量,(即每个实例。存在自己内存空间里的属性。)
实例变量展示
3>.公有属性:对应的就是类变量的属性
类的实例化以及类的赋值演示
4>.私有属性:表示不想被别人访问到的属性,只能在内部各函数中调用。隐藏一些功能的实现细节,只给外部暴露调用接口
七.继承
继承顺序
继承原理(python如何实现的继承)
Python 到底是如何实现继承的,对于你定义的每一个类,Python 会计算出一个方法解析顺序 (MRO) 列表,这个 MRO 列表就是一个简单的所有基类的线性顺序列表,例如
>>> F.mro() #等同于F.__mro__
[<class '__main__.F'>, <class '__main__.D'>, <class '__main__.B'>, <class '__main__.E'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]
为了实现继承,Python 会在 MRO 列表上从左到右开始查找基类,直到找到第一个匹配这个属性的类为止。
而这个 MRO 列表的构造是通过一个C3线性化算法来实现的。我们不去深究这个算法的数学原理,它实际上就是合并所有父类的 MRO 列表并遵循如下三条准则:
1.子类会先于父类被检查
2.多个父类会根据它们在列表中的顺序被检查
3.如果对下一个类存在两个合法的选择,选择第一个父类
子类中调用父类方法
子类继承了父类的方法,然后想进行修改,注意了是基于原有的基础上修改,那么就需要在子类中调用父类的方法
方法一:父类名.父类方法()
1 #!/usr/bin/env python
2 #_*_coding:utf-8_*_
3 #@author :yinzhengjie
4 #blog:#/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 Vehicle: # 定义交通工具类
7 Country = 'China'
8
9 def __init__(self, name, speed, load, power):
10 self.name = name
11 self.speed = speed
12 self.load = load
13 self.power = power
14
15 def run(self):
16 print('开动啦...')
17
18 class Subway(Vehicle): # 地铁
19 def __init__(self, name, speed, load, power, line):
20 Vehicle.__init__(self, name, speed, load, power)
21 self.line = line
22
23 def run(self):
24 print('地铁%s号线欢迎您' % self.line)
25 Vehicle.run(self)
26
27 line15 = Subway('中国地铁', '180m/s', '1000人/箱', '电', 15)
28 line15.run()
29
30
31 #以上代码执行结果如下:
32 地铁15号线欢迎您
33 开动啦...
方法二:super()
1 #!/usr/bin/env python
2 #_*_coding:utf-8_*_
3 #@author :yinzhengjie
4 #blog:#/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 Vehicle: # 定义交通工具类
7 Country = 'China'
8
9 def __init__(self, name, speed, load, power):
10 self.name = name
11 self.speed = speed
12 self.load = load
13 self.power = power
14 def run(self):
15 print('开动啦...')
16 class Subway(Vehicle): # 地铁
17 def __init__(self, name, speed, load, power, line):
18 # super(Subway,self) 就相当于实例本身 在python3中super()等同于super(Subway,self)
19 super().__init__(name, speed, load, power)
20 self.line = line
21 def run(self):
22 print('地铁%s号线欢迎您' % self.line)
23 super(Subway, self).run()
24 class Mobike(Vehicle): # 摩拜单车
25 pass
26 line13 = Subway('中国地铁', '180m/s', '1000人/箱', '电', 13)
27 line13.run()
28
29 #以上代码执行结果如下:
30 地铁13号线欢迎您
31 开动啦...
不用super引发的惨案
多继承方法案例
当你使用 super() 函数时,Python 会在 MRO 列表上继续搜索下一个类。只要每个重定义的方法统一使用 super() 并只调用它一次,那么控制流最终会遍历完整个 MRO 列表,每个方法也只会被调用一次(注意注意注意:使用 super 调用的所有属性,都是从 MRO 列表当前的位置往后找,千万不要通过看代码去找继承关系,一定要看 MRO 列表)
巧用__mro__方法展示
面向对象编程 (OOP) 语言的一个主要功能就是“继承”。继承是指这样一种能力:它可以使用现有类的所有功能,并在无需重新编写原来的类的情况下对这些功能进行扩展。
通过继承创建的新类称为“子类”或“派生类”。
被继承的类称为“基类”、“父类”或“超类”。
继承的过程,就是从一般到特殊的过程。
要实现继承,可以通过“继承”(Inheritance)和“组合”(Composition)来实现。
在某些 OOP 语言中,一个子类可以继承多个基类。但是一般情况下,一个子类只能有一个基类,要实现多重继承,可以通过多级继承来实现。
继承概念的实现方式主要有2类:实现继承、接口继承。
1>.实现继承是指使用基类的属性和方法而无需额外编码的能力;
2>. 接口继承是指仅使用属性和方法的名称、但是子类必须提供实现的能力(子类重构爹类方法);
在考虑使用继承时,有一点需要注意,那就是两个类之间的关系应该是“属于”关系。例如,Employee (员工) 是一个人,Manager(领导) 也是一个人,因此这两个类都可以继承 Person 类。但是 Leg(推) 类却不能继承 Person(人) 类,因为腿并不是一个人。
抽象类仅定义将由子类创建的一般属性和方法。
OOP开发范式大致为:划分对象→抽象类→将类组织成为层次化结构(继承和合成) →用类与实例进行设计和实现几个阶段。
完全继承展示
重新构造函数
重新构造函数升级版本,自动统计学校的人数
分析构方法用法展示
八.组合
软件重用的重要方式除了继承之外还有另外一种方式,即:组合
组合指的是,在一个类中以另外一个类的对象作为数据属性,称为类的组合
用组合的方式建立了类与组合的类之间的关系,它是一种‘有’的关系,比如教授有生日,教授教 Python 课程。当类之间有显著不同,并且较小的类是较大的类所需要的组件时,用组合比较好。
九.接口与归一化设计
1.什么是接口
java 中的 interface
继承有两种用途:
1>:继承基类的方法,并且做出自己的改变或者扩展(代码重用)
2>:声明某个子类兼容于某基类,定义一个接口类 Interface,接口类中定义了一些接口名(就是函数名)且并未实现接口的功能,子类继承接口类,并且实现接口中的功能
class Interface:#定义接口Interface类来模仿接口的概念,python中压根就没有interface关键字来定义一个接口。
def read(self): #定接口函数read
pass
def write(self): #定义接口函数write
pass
class Txt(Interface): #文本,具体实现read和write
def read(self):
print('文本数据的读取方法')
def write(self):
print('文本数据的读取方法')
class Sata(Interface): #磁盘,具体实现read和write
def read(self):
print('硬盘数据的读取方法')
def write(self):
print('硬盘数据的读取方法')
class Process(All_file):
def read(self):
print('进程数据的读取方法')
def write(self):
print('进程数据的读取方法')
实践中,继承的第一种含义意义并不很大,甚至常常是有害的。因为它使得子类与基类出现强耦合。
继承的第二种含义非常重要。它又叫“接口继承”。
接口继承实质上是要求“做出一个良好的抽象,这个抽象规定了一个兼容接口,使得外部调用者无需关心具体细节,可一视同仁的处理实现了特定接口的所有对象”——这在程序设计上,叫做归一化。
归一化使得高层的外部使用者可以不加区分的处理所有接口兼容的对象集合——就好象 Linux 的泛文件概念一样,所有东西都可以当文件处理,不必关心它是内存、磁盘、网络还是屏幕(当然,对底层设计者,当然也可以区分出“字符设备”和“块设备”,然后做出针对性的设计:细致到什么程度,视需求而定)。
在 Python 中根本就没有一个叫做 interface 的关键字,上面的代码只是看起来像接口,其实并没有起到接口的作用,子类完全可以不用去实现接口 ,如果非要去模仿接口的概念,可以借助第三方模块:#/pypi/zope.interface 中 twisted 的twisted\internet\interface.py 里使用 zope.interface
文档 #/en/latest/
设计模式:https://github.com/faif/python-patterns
2. 为何要用接口
接口提取了一群类共同的函数,可以把接口当做一个函数的集合。然后让子类去实现接口中的函数。这么做的意义在于归一化,什么叫归一化,就是只要是基于同一个接口实现的类,那么所有的这些类产生的对象在使用时,从用法上来说都一样。归一化,让使用者无需关心对象的类是什么,只需要的知道这些对象都具备某些功能就可以了,这极大地降低了使用者的使用难度。
比如:我们定义一个动物接口,接口里定义了有跑、吃、呼吸等接口函数,这样老鼠的类去实现了该接口,松鼠的类也去实现了该接口,由二者分别产生一只老鼠和一只松鼠送到你面前,即便是你分别不到底哪只是什么鼠你肯定知道他俩都会跑,都会吃,都能呼吸。再比如:我们有一个汽车接口,里面定义了汽车所有的功能,然后由本田汽车的类,奥迪汽车的类,大众汽车的类,他们都实现了汽车接口,这样就好办了,大家只需要学会了怎么开汽车,那么无论是本田,还是奥迪,还是大众我们都会开了,开的时候根本无需关心我开的是哪一类车,操作手法(函数调用)都一样
十.抽象类
什么是抽象类:
与 java 一样,Python 也有抽象类的概念但是同样需要借助模块实现,抽象类是一个特殊的类,它的特殊之处在于只能被继承,不能被实例化。
为什么要有抽象类:
如果说类是从一堆对象中抽取相同的内容而来的,那么抽象类就是从一堆类中抽取相同的内容而来的,内容包括数据属性和函数属性。 比如我们有香蕉的类,有苹果的类,有桃子的类,从这些类抽取相同的内容就是水果这个抽象的类,你吃水果时,要么是吃一个具体的香蕉,要么是吃一个具体的桃子。。。。。。你永远无法吃到一个叫做水果的东西。从设计角度去看,如果类是从现实对象抽象而来的,那么抽象类就是基于类抽象而来的。从实现角度来看,抽象类与普通类的不同之处在于:抽象类中只能有抽象方法(没有实现功能),该类不能被实例化,只能被继承,且子类必须实现抽象方法。这一点与接口有点类似,但其实是不同的,即将揭晓答案
其实抽象类说直白了就是让你们的程序更加的规范化:
抽象类的应用
抽象类与接口
抽象类的本质还是类,指的是一组类的相似性,包括数据属性(如 all_type )和函数属性(如 read、write ),而接口只强调函数属性的相似性。抽象类是一个介于类和接口直接的一个概念,同时具备类和接口的部分特性,可以用来实现归一化设计
十一.多态
多态性( polymorphisn )是允许你将父对象设置成为和一个或更多的他的子对象相等的技术,赋值之后,父对象就可以根据当前赋值给它的子对象的特性以不同的方式运作。简单的说,就是一句话:允许将子类类型的指针赋值给父类类型的指针。
那么,多态的作用是什么呢?我们知道,封装可以隐藏实现细节,使得代码模块化;继承可以扩展已存在的代码模块(类);它们的目的都是为了——代码重用。而多态则是为了实现另一个目的——接口重用!多态的作用,就是为了类在继承和派生的时候,保证使用“家谱”中任一类的实例的某一属性时的正确调用。
多态:我们可以这么理解,就是一种事物的不同形态如序列类型包含:字符串,列表,元组;
多态性:一个接口函数有多种实现方式。
Pyhon 很多语法都是支持多态的,比如 len(),sorted(), 你给 len 传字符串就返回字符串的长度,传列表就返回列表长度。
十二.静态方法和类方法
通常情况下,在类中定义的所有函数(注意了,这里说的就是所有,跟 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:#/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:#/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:#/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:#/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:#/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:#/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:#/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__"表示类的描述信息
2>.__module__ 和 __class__
假设存在以下目录结构:
test.py代码内容
__module__.py代码内容
importlib用法展示
3>.__init__ 构造方法,通过类创建对象时,自动触发执行。
4>.__del__析构方法,当对象在内存中被释放时,自动触发执行。
注:此方法一般无须定义,因为 Python 是一门高级语言,程序员在使用时无需关心内存的分配和释放,因为此工作都是交给 Python 解释器来执行,所以,析构函数的调用是由解释器在进行垃圾回收时自动触发执行的
5>. __call__ 对象后面加括号,触发执行。
注:构造方法的执行是由创建对象触发的,即:对象 = 类名() ;而对于 __call__ 方法的执行是由对象后加括号触发的,即:对象() 或者 类()()
'__call__'用法展示
6>. __dict__ 查看类或对象中的所有成员
__dict__用法展示
7>.__str__ 如果一个类中定义了__str__方法,那么在打印 对象 时,默认输出该方法的返回值。
__str__用法展示
8>.__getitem__、__setitem__、__delitem__
用于索引操作,如字典。以上分别表示获取、设置、删除数据
__getitem__,__setitem__和__delitem__用法展示
9>.__new__ 和 __metaclass__
1 #!/usr/bin/env python
2 #_*_coding:utf-8_*_
3 #@author :yinzhengjie
4 #blog:#/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:#/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 尹正杰
So ,要记住类 是由 type 类实例化产生
那么问题来了,类默认是由 type 类实例化产生,type类中如何实现的创建类?类又是如何创建对象?
答:类中有一个属性 __metaclass__,其用来表示该类由 谁 来实例化创建,所以,我们可以为 __metaclass__ 设置一个type类的派生类,从而查看 类 创建的过程。
自定义元类
类的生成 调用 顺序依次是 __new__ --> __init__ --> __call__
metaclass 详解文章:#/questions/100003/what-is-a-metaclass-in-python 得票最高那个答案写的非常好
本文内容转载自网络,本着分享与传播的原则,版权归原作者所有,如有侵权请联系我们进行删除!
填写下面表单即可预约申请免费试听! 怕学不会?助教全程陪读,随时解惑!担心就业?一地学习,可全国推荐就业!
Copyright © 京ICP备08000853号-56 京公网安备 11010802029508号 达内时代科技集团有限公司 版权所有
Tedu.cn All Rights Reserved