面向对象编程


简称OOP,是一种程序设计思想。OOP把对象作为程序的基本单元,一个对象包含了数据和操作数据的函数。数据封装、继承和多态是面向对象的三大特点。

在Python中,所有数据类型都可以视为对象,当然也可以自定义对象。自定义的对象数据类型就是

面向对象中的类(Class)的概念。

给对象发消息实际上就是调用对象对应的关联函数,称之为对象的方法(Method)


OOP最重要的概念就是(Class)和实例(Instance),牢记类是抽象的模板,而实例是根据类创建出来的一个个具体的“对象”,每个对象都拥有相同的方法,但各自的数据可能不同。(艾维铝酉和cjk区别——#)

定义类通过class关键字

  1. class Student(object):
  2. pass
  3.  
  4. #object表示该类是从哪个类继承下来的,如果没有合适的继承类就使用object类,这是所有类最终都会继承的类。

创建实例通过类名+()

  1. bart = Student()

可以给实例的变量绑定属性

  1. bart.name = 'Bart Simpson'

由于类可以起到模板的作用,在创建实例的时候可以通过定义__init__方法把认为必要的属性强制填写进去绑定

  1. class Student(object):
  2.  
  3. def __init__(self, name, score): #将name和score属性绑定
  4. self.name = name
  5. self.score = score
  6.  
  7. #__init__方法的第一个参数永远是self,表示创建的实例本身,因此可以把各种属性绑定到self,因为self就指向创建的实例本身。

有了__init__方法,在创建实例的时候,就不能传入空的参数了而必须传入与__init__方法匹配的参数,self不需要传

  1. >>> bart = Student('Bart Simpson', 59)
  2. >>> bart.name
  3. 'Bart Simpson'
  4. >>> bart.score
  5. 59

在类中定义的函数和普通函数相比只有一点不同,就是第一个参数永远是实例变量self,并且调用时不用传递该参数。

访问实例的数据,没必要用外面的函数去访问,可以直接在类的内部定义访问数据的函数,这样,就把“数据”给封装起来了,称为数据封装。这些封装数据的函数是和类本身是关联起来的,称为类的方法

  1. class Student(object):
  2.  
  3. def __init__(self, name, score):
  4. self.name = name
  5. self.score = score
  6.  
  7. def print_score(self):
  8. print('%s: %s' % (self.name, self.score)) #调用方法>>>bart.print_score()

封装的一个好处是不用知道内部实现的细节容易调用,另一个是好处是可以给类增加新的方法

  1. class Student(object):
  2. ...
  3.  
  4. def get_grade(self): #给该类增加一个get_grade方法
  5. if self.score >= 90:
  6. return 'A'
  7. elif self.score >= 60:
  8. return 'B'
  9. else:
  10. return 'C'

类是创建实例的模板,而实例则是一个一个具体的对象,各个实例拥有的数据都互相独立,互不影响;

方法就是与实例绑定的函数,和普通函数不同,方法可以直接访问实例的数据(普通函数需要访问类);


使用上述方法可以隐藏内部复杂逻辑,而在属性名称前加上两个下划线__可以访问限制,使得外部代码不能自由修改实例的属性

  1. class Student(object):
  2.  
  3. def __init__(self, name, score): #之前>>> bart.score 98
  4. # >>> bart.score = 59
  5. 5 # >>> bart.score 59
  6. self.__name = name
  7. self.__score = score #之后>>> bart.__name Error
  8.  
  9. def print_score(self):
  10. print('%s: %s' % (self.__name, self.__score))

如果外部代码要获取属性,可以给类增加get_nameget_score这样的方法

  1. class Student(object):
  2. ...
  3.  
  4. def get_name(self):
  5. return self.__name
  6.  
  7. def get_score(self):
  8. return self.__score

如果又要允许外部代码修改属性,可以再给类增加set_score方法

  1. class Student(object):
  2. ...
  3.  
  4. def set_score(self, score):
  5. self.__score = score

类似__xxx__是特殊变量,可以直接访问,但是不要定义这样的变量名

一个下划线开头的变量,可以被外部访问,但是约定俗成视为私有变量,不要随意访问

两个下划线开头的变量,可以通过   实例名._类名__变量名   来访问(不同版本.后的可能不同)

总的来说就是,Python本身没有任何机制阻止你干坏事,一切全靠自觉。


OOP程序设计中定义一个class的时候,可以从某个现有的class继承,子类将获得了父类的全部功能(方法),也可以对子类增加方法。如果一个变量是子类的数据类型,那么它同时也是父类、爷类...类型,这就是多态

  1. class Animal(object): #编写一个名为Animal的class
  2. def run(self):
  3. print('Animal is running...')
  4.  
  5. class Dog(Animal): #直接从Animal类继承
  6. pass
  7.  
  8. 运行
  9. dog = Dog()
  10. dog.run()
  11. Animal is running...
  12.  
  13. class Dog(Animal): #对子类增加方法
  14.  
  15. def run(self):
  16. print('Dog is running...')
    18
    19 运行
    20 Dog is running... #调用时子类会覆盖与父类定义同名的方法

多态的好处

  1. def run_twice(animal): #定义一个接收Animal类型的函数
  2. animal.run() #调用函数时不管参数是父类Animal()其子类比如Dog(),实际调用的都是类中各自定义的叫 run 的方法,只要 实参是形参的子类,无需修改函数

注:

如果需要传入Animal类型,对于静态语言(如JAVA)传入的对象必须是Animal类型或其子类,否则无法调用run方法。对于动态语言(如Python),则不一定需要传入Animal类型,只需要保证传入的对象有一个run()方法就可以了。


获取对象信息

判断对象类型:基本类型都可以用type(),指向函数或者类的变量也可以用type()判断,返回对应的Class类型。

判断class的类型,可以使用isinstance( ,)函数,返回布尔值。

获得对象的所有属性和方法:可以使用dir()函数,返回一个包含字符串的list,

  1. >>> hasattr(obj, 'x') # 有属性'x'吗?
  2. True
  3. >>> obj.x
  4. 9
  5. >>> hasattr(obj, 'y') # 有属性'y'吗?
  6. False
  7. >>> setattr(obj, 'y', 19) # 设置一个属性'y'
  8. >>> hasattr(obj, 'y') # 有属性'y'吗?
  9. True
  10. >>> getattr(obj, 'y') # 获取属性'y'
  11. 19
  12. >>> obj.y # 获取属性'y'
  13. 19
  14. >>> fn = getattr(obj, 'y') # 获取属性'y'并赋值到变量fn
  15.  
  16. #通过内置的一系列函数,可以对任意一个Python对象进行剖析,拿到其内部的数据。

注:

由于Python是动态语言,根据类创建的实例可以任意绑定属性。


给实例绑定属性

  1. class Student(object): #通过self变量
  2. def __init__(self, name):
  3. self.name = name
  4.  
  5. s = Student('Bob') #通过实例变量
  6. s.score = 90

给类绑定属性

  1. class Student(object): #直接在class中定义属性
  2. name = 'Student'

实例属性类属性优先级高

给实例绑定方法

  1. >>> def set_age(self, age): # 定义一个函数作为实例方法
  2. ... self.age = age
  3. ...
  4. >>> from types import MethodType
  5. >>> s.set_age = MethodType(set_age, s) # 给实例绑定一个方法
  6. >>> s.set_age(25) # 调用实例方法
  7. >>> s.age # 测试结果
  8. 25

给类绑定方法,相当于给其所有实例绑定了方法

  1. >>> def set_score(self, score): #定义一个函数并把它绑定给类
  2. ... self.score = score
  3. ...
  4. >>> Student.set_score = set_score

限制实例的属性,在定义class的时候定义一个特殊的__slots__变量,比如只允许实例Student添加添加name和age属性:

  1. class Student(object):
  2. __slots__ = ('name', 'age') # 用tuple装允许绑定的属性名称

如果暴露性地绑定属性(绑定在定义外),无法检查绑定的参数是否合法,可以通过内置方法来解决,然后通过python内置的@property装饰器将getter方法变成属性简单的使用,同时另一个装饰器@.setter把绑值setter方法变成属性。

  1. class Student(object):
  2.  
  3. @property #编写获得值的方法并变成属性
  4. def score(self):
  5. return self._score
  6.  
  7. @score.setter #编写带自检的设值(绑定属性)方法并变成属性
  8. def score(self, value):
  9. if not isinstance(value, int):
  10. raise ValueError('score must be an integer!')
  11. if value < 0 or value > 100:
  12. raise ValueError('score must between 0 ~ 100!')
  13. self._score = value

如果只定义getter方法(@property),不定义setter方法(@.setter)就是一个只读属性

注:

动态绑定允许在程序运行的过程中动态给class加上功能,静态语言必须定义在class中。


面向对象高级编程


通过多重继承,一个子类就可以同时获得多个父类的所有功能。这种继承如果按树型层次继承是复杂度呈指数增长,所以采用称之为MixIn的设计。设计类的继承关系时,设计单一继承下来的主线,将需要混入的额外功能的父类名尾加MixIn,这样就可以优先考虑通过多重继承来组合多个MixIn的功能,而不是设计多层次的复杂的继承关系。

EXP Python自带了TCPServerUDPServer这两类网络服务,而要同时服务多个用户就必须使用多进程或多线程模型,这两种模型由ForkingMixInThreadingMixIn提供。通过组合,就可以创造出合适的服务来。

  1. class MyTCPServer(TCPServer, ForkingMixIn): #编写一个多进程模式的TCP服务
  2. pass
  3.  
  4. class MyUDPServer(UDPServer, ThreadingMixIn): #编写一个多线程模式的UDP服务
  5. pass

注:

只允许单一继承的语言(如Java)不能使用MixIn的设计。


定制类

枚举类:Enum类可以把一组相关常量定义在一个class中,即用枚举类型定义一个class,每个常量都是class的一个唯一实例。

  1. from enum import Enum #定义Monthe类型的枚举类
  2.  
  3. Month = Enum('Month', ('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec')) #可以用Month.XXX引用一个常量
  4.  
  5. for name, member in Month.__members__.items():
  6. print(name, '=>', member, ',', member.value)
  7. #枚举所有成员,value属性则是自动赋给成员的int常量,默认从1开始计数。
  8.  
  9. from enum import Enum, unique #可以通过从Enum派生出自定义类更精确的控制枚举
  10.  
  11. @unique #@unique装饰器可以帮助检查保证没有重复值。
  12. class Weekday(Enum):
  13. Sun = 0 # Sun的value被设定为0
  14. Mon = 1
  15. Tue = 2
  16. Wed = 3
  17. Thu = 4
  18. Fri = 5
  19. Sat = 6

type()函数既可以返回一个对象的类型,又可以创建出新的类型。

要创建一个class对象,type()函数依次传入3个参数:

  1. class的名称;
  2. 继承的父类集合,注意Python支持多重继承,如果只有一个父类,别忘了tuple的单元素写法;
  3. class的方法名称与函数绑定,这里我们把函数fn绑定到方法名hello上。
  1. >>> def fn(self, name='world'): # 先定义函数
  2. ... print('Hello, %s.' % name)
  3. ...
  4. >>> Hello = type('Hello', (object,), dict(hello=fn)) # 创建Hello class

通过type()函数创建的类和直接写class是完全一样的,但是type()函数也允许我们动态创建出类来。

元类:除了使用type()动态创建类以外,要控制类的创建行为,还可以使用metaclass,直译为元类,简单的解释就是:先定义metaclass,就可以创建类,最后创建实例,可以把类看成是metaclass创建出来的“实例”。

metaclass是Python面向对象里最难理解,也是最难使用的魔术代码,它可以改变类创建时的行为,功能强大,但不常用,使用起来务必小心。

python advanced programming ( II )的更多相关文章

  1. python advanced programming (Ⅲ)

    IO编程 IO在计算机中指Input/Output.由于程序和运行时数据是在内存中驻留,由CPU来执行,涉及到数据交换的地方,通常是磁盘.网络等,就需要IO接口. IO编程中,Stream(流)是一个 ...

  2. python advanced programming ( I )

    函数式编程 函数是Python内建支持的一种封装,通过把大段代码拆成函数,通过一层一层的函数调用,就可以把复杂任务分解成简单的任务,这种分解可以称之为面向过程的程序设计.函数就是面向过程的程序设计的基 ...

  3. python's descriptor II

    [python's descriptor II] For instance, a.x has a lookup chain starting with a.__dict__['x'], then ty ...

  4. python network programming tutorial

    关于网络编程以及socket 等一些概念和函数介绍就不再重复了,这里示例性用python 编写客户端和服务器端. 一.最简单的客户端流程: 1. Create a socket 2. Connect ...

  5. python初步要点II

    [python初步要点II] 1.is & is not 操作符用于测试2个对象是否指向同一个对象,即 id(a) == id(b). 2.整形和字符串对象是不可变对象,python会高效地缓 ...

  6. Python 高级网络操作 - Python Advanced Network Operations

    Python 高级网络操作 - Python Advanced Network Operations Half Open Socket, 一个单向的 socket 被称为 half open sock ...

  7. [Python] Advanced features

    Slicing 12345 L[:10:2] # [0, 2, 4, 6, 8]L[::5] # 所有数,每5个取一个# [0, 5, 10, 15, 20, 25, 30, 35, 40, 45, ...

  8. #3使用html+css+js制作网页 番外篇 使用python flask 框架 (II)

    #3使用html+css+js制作网页 番外篇 使用python flask 框架 II第二部 0. 本系列教程 1. 登录功能准备 a.python中操控mysql b. 安装数据库 c.安装mys ...

  9. Python Socket Programming

    本文介绍使用Python进行Socket网络编程,假设读者已经具备了基本的网络编程知识和Python的基本语法知识,本文中的代码如果没有说明则都是运行在Python 3.4下. Python的sock ...

随机推荐

  1. props传递数据

    一.传递数据 1.props 传入单数据 就像 data 一样,prop 可以用在模板内,同样也可以在 vm 实例中像“this.message”这样使用 <template> <d ...

  2. An enumerable sequence of parameters (arrays, lists, etc) is not allo

    环境:dapper asp.net core 出错代码如下: public Task<IEnumerable<dynamic>> GetList(string query, p ...

  3. Linux驱动之LED驱动编写

    从上到下,一个软件系统可以分为:应用程序.操作系统(内核).驱动程序.结构图如下:我们需要做的就是写出open.read.write等驱动层的函数.一个LED驱动的步骤如下: 1.查看原理图,确定需要 ...

  4. ofo退押金脚本

    同事钉钉给的 因为押金一直没退,电话很难打进去,咨询客服排队要等好久,一直几千位. 长时间挂机就自动退出客服了,所以自动写了一个脚本,目前已经成功退押金了.所以共享出来 1.关注ofo小黄车订阅号,注 ...

  5. python使用sqlite

    摘自python帮助文档 一.基本用法 import sqlite3 conn = sqlite3.connect('example.db')#conn = sqlite3.connect(':mem ...

  6. (转)ScriptManager.RegisterStartupScript方法和Page.ClientScript.RegisterStartupScript() 方法

    ScriptManager.RegisterStartupScript方法 如果页面中不用Ajax,cs中运行某段js代码方式可以是: Page.ClientScript.RegisterStartu ...

  7. BZOJ1855或洛谷2569 [SCOI2010]股票交易

    一道单调队列优化\(DP\) BZOJ原题链接 洛谷原题链接 朴素的\(DP\)方程并不难想. 定义\(f[i][j]\)表示到第\(i\)天,手上持有\(j\)股时的最大收益. 转移方程可以分成四个 ...

  8. 会调色了不起吗? SORRY,会调色真的了不起!

    其实,现实的世界,大部分都是非常普通和常见的.所以调色师才有他们发挥的空间.如何把镜头中的世界变成梦幻一般. 把画面的颜色统一之后,逼格马上提升了很多! 发现表情不对,从其他照片把表情P回来,哈哈 这 ...

  9. [ES]elasticsearch章2 ES查询过程解析

    es服务端是准确知道每个document分布在哪个shard上: search一个比较复杂的执行模式,因为我们不知道那些document会被匹配到,任何一个shard上都有可能,所以一个search请 ...

  10. merge_关系

    create(olive:Person{chau:"Bill White",name:"Olive Stone",bornin:"New York&q ...