• 第7章 面向对象


    一、面向过程和面向对象

    面向过程的程序设计:核心是过程二字,过程指的是解决问题的步骤,即先干什么再干什么......面向过程的设计就好比精心设计好一条流水线,是一种机械式的思维方式。

    优点:复杂度的问题流程化,进而简单化。

    缺点:可扩展性差。

    应用场景:脚本程序,比如linux系统管理脚本,著名案例:linux内核,httpd,git

    面向对象:核心是对象二字,对象就是特征与技能的结合体,与面向过程对机械流水的模拟形式鲜明的对比面向对象更加注重的对现实世界的模拟。

    优点:可扩展性高。

    缺点:

    1、编程的复杂度远高于面向过程,不了解面向对象而立即上手基于它设计程序,极容易出现过度设计的问题,一些扩展性要求低的场景使用面向对象会徒增编程难度,比如管理linux系统的shell脚本就不适合用面向对象去设计,面向过程反而更加适合。

    2、无法向面向过程的程序设计流水线式的可以很精准问题的处理流程与结果,面向对象的程序一旦对象的程序一旦开始就由对象之间的交互解决问题,即便是上帝也无法准确的预测最终的结果,于是我们经常看到对战类游戏,新增一个游戏人物,在对战的过程中极容易出现阴霸的技能,一刀砍死3个人,这种情况是无法准确预知的,只有对象之间交互才能准确地知道最终的结果。
    应用场景:需求经常变化的软件,一般需求的变化都集中在用户层,互联网应用,企业内部软件,游戏等都是面向对象的程序设计大显身手的好地方。

     面向对象的程序设计并不是全部,对于一个软件质量来说,面向对象的程序设计只是用来解决扩展性。

    二、类与对象

    类即类别、种类,是面向对象设计最重要的概念,对象是特征与技能的结合体,而类则是一系列对象相似的特征与技能的结合体。

    在现实世界中:先有对象,再有类

    在程序中,务必保证先定义类,后产生对象。

    这与函数的使用是类似的,先定义函数,后调用函数,类也是一样的,在程序中需要先定义类,后调用类,不一样的是,调用函数会执行函数体代码返回的是函数体执行的结果,而调用类会产生对象,返回的是对象。

    按照上述步骤,我们来定义一个类

     1 #在现实世界中,站在老男孩学校的角度:先有对象,再有类
     2 对象1:李坦克
     3     特征:
     4         学校=oldboy
     5         姓名=李坦克
     6         性别= 7         年龄=18
     8     技能:
     9         学习
    10         吃饭
    11         睡觉
    12 
    13 对象2:王大炮
    14     特征:
    15         学校=oldboy
    16         姓名=王大炮
    17         性别=18         年龄=38
    19     技能:
    20         学习
    21         吃饭
    22         睡觉
    23 
    24 对象3:牛榴弹
    25     特征:
    26         学校=oldboy
    27         姓名=牛榴弹
    28         性别=29         年龄=78
    30     技能:
    31         学习
    32         吃饭
    33         睡觉
    34 
    35 
    36 现实中的老男孩学生类
    37     相似的特征:
    38         学校=oldboy
    39     相似的技能:
    40         学习
    41         吃饭
    42         睡觉
    43 
    44 在现实世界中:先有对象,再有类
    在现实世界中:先有对象,再有类
     1 #在程序中,务必保证:先定义(类),后使用(产生对象)
     2 PS:
     3   1. 在程序中特征用变量标识,技能用函数标识
     4   2. 因而类中最常见的无非是:变量和函数的定义
     5 
     6 #程序中的类
     7 class OldboyStudent:
     8     school='oldboy'
     9     def learn(self):
    10         print('is learning')
    11         
    12     def eat(self):
    13         print('is eating')
    14     
    15     def sleep(self):
    16         print('is sleeping')
    17   
    18 
    19 
    20 #注意:
    21   1.类中可以有任意python代码,这些代码在类定义阶段便会执行
    22   2.因而会产生新的名称空间,用来存放类的变量名与函数名,可以通过OldboyStudent.__dict__查看
    23   3.对于经典类来说我们可以通过该字典操作类名称空间的名字(新式类有限制),但python为我们提供专门的.语法
    24   4.点是访问属性的语法,类中定义的名字,都是类的属性
    25 
    26 #程序中类的用法
    27 .:专门用来访问属性,本质操作的就是__dict__
    28 OldboyStudent.school #等于经典类的操作OldboyStudent.__dict__['school']
    29 OldboyStudent.school='Oldboy' #等于经典类的操作OldboyStudent.__dict__['school']='Oldboy'
    30 OldboyStudent.x=1 #等于经典类的操作OldboyStudent.__dict__['x']=1
    31 del OldboyStudent.x #等于经典类的操作OldboyStudent.__dict__.pop('x')
    32 
    33 
    34 #程序中的对象
    35 #调用类,或称为实例化,得到对象
    36 s1=OldboyStudent()
    37 s2=OldboyStudent()
    38 s3=OldboyStudent()
    39 
    40 #如此,s1、s2、s3都一样了,而这三者除了相似的属性之外还各种不同的属性,这就用到了__init__
    41 #注意:该方法是在对象产生之后才会执行,只用来为对象进行初始化操作,可以有任意代码,但一定不能有返回值
    42 class OldboyStudent:
    43     ......
    44     def __init__(self,name,age,sex):
    45         self.name=name
    46         self.age=age
    47         self.sex=sex
    48     ......
    49 
    50 
    51 s1=OldboyStudent('李坦克','',18) #先调用类产生空对象s1,然后调用OldboyStudent.__init__(s1,'李坦克','男',18)
    52 s2=OldboyStudent('王大炮','',38)
    53 s3=OldboyStudent('牛榴弹','',78)
    54 
    55 
    56 #程序中对象的用法
    57 #执行__init__,s1.name='牛榴弹',很明显也会产生对象的名称空间
    58 s2.__dict__
    59 {'name': '王大炮', 'age': '', 'sex': 38}
    60 
    61 s2.name #s2.__dict__['name']
    62 s2.name='王三炮' #s2.__dict__['name']='王三炮'
    63 s2.course='python' #s2.__dict__['course']='python'
    64 del s2.course #s2.__dict__.pop('course')
    65 
    66 在程序中:先定义类,后产生对象
    在程序中:先定义类,后产生对象

     ps:

    1、站的角度不同,定义出的类是截然不同的

    2、现实中类并不完全等于程序中类,比如现实中的公司类,在程序中有时需要拆分成部门类,业务类......

    3、有时为了编程需求,程序中也可能会定义现实中不存在的类,比如策略类,现实中并不存在,但是在程序中却是一个很常见的类。

     1 #python为类内置的特殊属性
     2 类名.__name__# 类的名字(字符串)
     3 类名.__doc__# 类的文档字符串
     4 类名.__base__# 类的第一个父类(在讲继承时会讲)
     5 类名.__bases__# 类所有父类构成的元组(在讲继承时会讲)
     6 类名.__dict__# 类的字典属性
     7 类名.__module__# 类定义所在的模块
     8 类名.__class__# 实例对应的类(仅新式类中)
     9 
    10 类的特殊属性(了解即可)
    类的特殊属性

    三、属性查找

    类有两种属性:数据属性和函数属性

    1、类的数据属性是所有对象共享的

    2、类的函数属性是绑定给对象用的

     1 school='hahahahahahaah'
     2 class OldboyStudent:
     3     # school = 'oldboy'
     4 
     5     def __init__(self,name,age,sex):
     6         self.name=name
     7         self.age=age
     8         self.sex=sex
     9 
    10     def learn(self):
    11         print('%s is learning'  %self.name)
    12 
    13     def eat(self):
    14         print('is eating')
    15 
    16 obj1=OldboyStudent('李大炮',18,'')
    17 obj2=OldboyStudent('张全蛋',28,'')
    18 obj3=OldboyStudent('牛榴弹',18,'')
    19 print(obj1.__dict__)
    20 
    21 print(obj1.name,obj1.age,obj1.sex)
    22 #对象可以访问类的数据属性,结论:类的数据属性共享给所有对象使用,id对一样
    23 print(obj1.school,id(obj1.school))
    24 print(obj2.school,id(obj2.school))
    25 print(obj3.school,id(obj3.school))
    26 print(OldboyStudent.school,id(OldboyStudent.school))
    27 
    28 #类的函数属性是绑定给所有对象使用的,绑定给不同的对象是不同的绑定方法,绑定方法有何特殊之处?
    29 #暂且抛开绑定方法,类肯定可以访问自己的函数属性
    30 OldboyStudent.learn(obj1)
    31 OldboyStudent.learn(obj2)
    32 OldboyStudent.learn(obj3)
    33 
    34 print(obj1.learn)
    35 print(obj2.learn)
    36 print(obj3.learn)
    37 print(OldboyStudent.learn)

    在obj.name会先从obj自己的名称空间里找name,找不到则去类中找,类也找不到就找父类....最后都找不到就抛出异常。

    练习:编写一个学生类,产生一堆学生对象,要求有一个计数器(属性),统计总共实例了多少个对象。

     1 class OldboyStudent:
     2     school = 'oldboy'
     3     cont = 0
     4 
     5     def learn(self):
     6         print('is learning')
     7 
     8     def __init__(self,name,age,sex):
     9         self.name=name
    10         self.age=age
    11         self.sex=sex
    12         OldboyStudent.cont+=1
    13     def eat(self):
    14         print('is eating')
    15 
    16     def sleep(self):
    17         print('is sleeping')
    View Code

    四、绑定到对象的方法的特殊之处

     1 #改写
     2 class OldboyStudent:
     3     school='oldboy'
     4     def __init__(self,name,age,sex):
     5         self.name=name
     6         self.age=age
     7         self.sex=sex
     8     def learn(self):
     9         print('%s is learning' %self.name) #新增self.name
    10 
    11     def eat(self):
    12         print('%s is eating' %self.name)
    13 
    14     def sleep(self):
    15         print('%s is sleeping' %self.name)
    16 
    17 
    18 s1=OldboyStudent('李坦克','',18)
    19 s2=OldboyStudent('王大炮','',38)
    20 s3=OldboyStudent('牛榴弹','',78)

    类中定义的函数(没有被任何装饰器装饰的)是类的函数属性,类可以使用,但必须遵循的参数规则,有几个参数需要传几个参数

    1 OldboyStudent.learn(s1) #李坦克 is learning
    2 OldboyStudent.learn(s2) #王大炮 is learning
    3 OldboyStudent.learn(s3) #牛榴弹 is learning

    类中定义的函数(没有被任何装饰器装饰的),其实主要是给对象使用的,而且是绑定到对象的,虽然所有对象指向的都是相同的功能,但是绑定到不同对象就是不同的绑定方法。

    强调:绑定到对象的方法的特殊之处在于,绑定给谁就由谁来调用,谁来调用,就会将“谁”本身当做第一个参数传给方法,即自动传值(方法__init__也是一样的道理)

    1 s1.learn() #等同于OldboyStudent.learn(s1)
    2 s2.learn() #等同于OldboyStudent.learn(s2)
    3 s3.learn() #等同于OldboyStudent.learn(s3)

    注意:绑定到对象的方法的这种自动传值的特征,决定了在类的定义的函数都要默认写一个参数self,self可以是任意名字,但是约定俗称地写出self。

    类即类型

    提示:python的class术语与c++有一定区别,与Modula-3更像。

    python中一切皆为对象,且python3中类与类型是一个概念,类型就是类

     1 #类型dict就是类dict
     2 >>> list
     3 <class 'list'>
     4 
     5 #实例化的到3个对象l1,l2,l3
     6 >>> l1=list()
     7 >>> l2=list()
     8 >>> l3=list()
     9 
    10 #三个对象都有绑定方法append,是相同的功能,但内存地址不同
    11 >>> l1.append
    12 <built-in method append of list object at 0x10b482b48>
    13 >>> l2.append
    14 <built-in method append of list object at 0x10b482b88>
    15 >>> l3.append
    16 <built-in method append of list object at 0x10b482bc8>
    17 
    18 #操作绑定方法l1.append(3),就是在往l1添加3,绝对不会将3添加到l2或l3
    19 >>> l1.append(3)
    20 >>> l1
    21 [3]
    22 >>> l2
    23 []
    24 >>> l3
    25 []
    26 #调用类list.append(l3,111)等同于l3.append(111)
    27 >>> list.append(l3,111) #l3.append(111)
    28 >>> l3
    29 [111]

    五、对象之间的交互

    例如:

    1 class Garen:        #定义英雄盖伦的类,不同的玩家可以用它实例出自己英雄;
    2     camp='Demacia'  #所有玩家的英雄(盖伦)的阵营都是Demacia;
    3     def __init__(self,nickname,aggressivity=58,life_value=455): #英雄的初始攻击力58...;
    4         self.nickname=nickname  #为自己的盖伦起个别名;
    5         self.aggressivity=aggressivity #英雄都有自己的攻击力;
    6         self.life_value=life_value #英雄都有自己的生命值;
    7     def attack(self,enemy):   #普通攻击技能,enemy是敌人;
    8         enemy.life_value-=self.aggressivity #根据自己的攻击力,攻击敌人就减掉敌人的生命值。

    可以仿照garen类再创建一个Riven类

    1 class Riven:
    2     camp='Noxus'  #所有玩家的英雄(锐雯)的阵营都是Noxus;
    3     def __init__(self,nickname,aggressivity=54,life_value=414): #英雄的初始攻击力54;
    4         self.nickname=nickname  #为自己的锐雯起个别名;
    5         self.aggressivity=aggressivity #英雄都有自己的攻击力;
    6         self.life_value=life_value #英雄都有自己的生命值;
    7     def attack(self,enemy):   #普通攻击技能,enemy是敌人;
    8         enemy.life_value-=self.aggressivity #根据自己的攻击力,攻击敌人就减掉敌人的生命值。

    实例出俩英雄

    1 g1=Garen('test')
    2 r1=Riven('test1')

    交互:test1攻击test,反之一样

    1 print(r1.life_value)
    2 g1.attack(r1)
    3 print(r1.life_value)

    六、面向对象之继承

    1、什么是继承

    继承指的是类与类之间的关系,是一种什么是什么的关系,功能之一就是用来解决代码重用问题。

    继承是一种创建新类的方式,在python中,新建的类可以继承一个或多个父类,父类又可称为基类或超类,新建的类称为派生类或子类。

    python中类的继承分为:单继承和多继承

     1 class ParentClass1: #定义父类
     2     pass
     3 
     4 class ParentClass2: #定义父类
     5     pass
     6 
     7 class SubClass1(ParentClass1): #单继承,基类是ParentClass1,派生类是SubClass
     8     pass
     9 
    10 class SubClass2(ParentClass1,ParentClass2): #python支持多继承,用逗号分隔开多个继承的类
    11     pass

    2、查看继承

    1 print(SubClass1.__bases__)  #__base__只查看从左到右继承的第一个子类,__bases__则是查看所有继承的父类
    2 print(SubClass2.__bases__)  #(<class '__main__.ParentClass1'>, <class '__main__.ParentClass2'>)
    3 
    4 print(SubClass2.mro())    #为了实现继承,python会在MRO列表上从左到右开始查找基类,直到找到第一个匹配这个属性的类为止。
    5 #[<class '__main__.SubClass2'>, <class '__main__.ParentClass1'>, <class '__main__.ParentClass2'>, <class 'object'>]

     3、经典类与新式类

    1.只有在python2中才分新式类和经典类,python3中统一都是新式类
    2.在python2中,没有显式的继承object类的类,以及该类的子类,都是经典类
    3.在python2中,显式地声明继承object的类,以及该类的子类,都是新式类
    3.在python3中,无论是否继承object,都默认继承object,即python3中所有类均为新式类

    提示:如果没有指定基类,python的类会默认继承object类,object是所有python类的基类,它提供了一些常见方法(如__str__)的实现

    1 print(ParentClass1.__bases__) #(<class 'object'>,)
    2 print(ParentClass2.__bases__)  #(<class 'object'>,)

    七、继承与抽象(先抽象再继承)

    抽象即抽取类似或者说比较像的部分。

    抽象分成两个层次:

    1、将奥巴马和梅西这俩对象比较像的部分抽取成类;

    2、将人,猪,狗这三个比较像的部分抽取成父类。

    抽象最主要的作用是划分类别(可以隔离关注点,降低复杂度)

    继承:是基于抽象的结果,通过编程语言去实现它,肯定是先经历抽象这个过程,才能通过继承的方式去表达出抽象的结构。

    抽象只是分析和设计的过程中,一个动作或者一种技巧,通过抽象可以得到类。

    八、继承与重用性

      1 ==========================第一部分
      2 例如
      3 
      4   猫可以:喵喵叫、吃、喝、拉、撒
      5 
      6   狗可以:汪汪叫、吃、喝、拉、撒
      7 
      8 如果我们要分别为猫和狗创建一个类,那么就需要为 猫 和 狗 实现他们所有的功能,伪代码如下:
      9  
     10 
     11 #猫和狗有大量相同的内容
     12 class 猫:
     13 
     14     def 喵喵叫(self):
     15         print '喵喵叫'
     16 
     17     def 吃(self):
     18         # do something
     19 
     20     def 喝(self):
     21         # do something
     22 
     23     def 拉(self):
     24         # do something
     25 
     26     def 撒(self):
     27         # do something
     28 
     29 class 狗:
     30 
     31     def 汪汪叫(self):
     32         print '喵喵叫'
     33 
     34     def 吃(self):
     35         # do something
     36 
     37     def 喝(self):
     38         # do something
     39 
     40     def 拉(self):
     41         # do something
     42 
     43     def 撒(self):
     44         # do something
     45 
     46 
     47 
     48 ==========================第二部分
     49 上述代码不难看出,吃、喝、拉、撒是猫和狗都具有的功能,而我们却分别的猫和狗的类中编写了两次。如果使用 继承 的思想,如下实现:
     50 
     51   动物:吃、喝、拉、撒
     52 
     53      猫:喵喵叫(猫继承动物的功能)
     54 
     55      狗:汪汪叫(狗继承动物的功能)
     56 
     57 伪代码如下:
     58 class 动物:
     59 
     60     def 吃(self):
     61         # do something
     62 
     63     def 喝(self):
     64         # do something
     65 
     66     def 拉(self):
     67         # do something
     68 
     69     def 撒(self):
     70         # do something
     71 
     72 # 在类后面括号中写入另外一个类名,表示当前类继承另外一个类
     73 class 猫(动物):
     74 
     75     def 喵喵叫(self):
     76         print '喵喵叫'
     77         
     78 # 在类后面括号中写入另外一个类名,表示当前类继承另外一个类
     79 class 狗(动物):
     80 
     81     def 汪汪叫(self):
     82         print '喵喵叫'
     83 
     84 
     85 
     86 
     87 ==========================第三部分
     88 #继承的代码实现
     89 class Animal:
     90 
     91     def eat(self):
     92         print("%s 吃 " %self.name)
     93 
     94     def drink(self):
     95         print ("%s 喝 " %self.name)
     96 
     97     def shit(self):
     98         print ("%s 拉 " %self.name)
     99 
    100     def pee(self):
    101         print ("%s 撒 " %self.name)
    102 
    103 
    104 class Cat(Animal):
    105 
    106     def __init__(self, name):
    107         self.name = name
    108         self.breed = ''
    109 
    110     def cry(self):
    111         print('喵喵叫')
    112 
    113 class Dog(Animal):
    114 
    115     def __init__(self, name):
    116         self.name = name
    117         self.breed=''
    118 
    119     def cry(self):
    120         print('汪汪叫')
    121 
    122 
    123 # ######### 执行 #########
    124 
    125 c1 = Cat('小白家的小黑猫')
    126 c1.eat()
    127 
    128 c2 = Cat('小黑的小白猫')
    129 c2.drink()
    130 
    131 d1 = Dog('胖子家的小瘦狗')
    132 d1.eat()
    133 
    134 使用继承来重用代码比较好的例子

    在开发程序的过程中,如果我们定义了一个类A,然后又想新建另外一个类,但是类B的大部分内容与类A的相同时

    我们不可能从头开始写一个类B,这就用到了类的继承的概念。

    通过继承的方式新建类B,让B继承A,B会‘遗传’A的所有属性(数据属性和函数属性),实现代码重用

     1 class Hero:
     2     def __init__(self,nickname,aggressivity,life_value):
     3         self.nickname=nickname
     4         self.aggressivity=aggressivity
     5         self.life_value=life_value
     6 
     7     def move_forward(self):
     8         print('%s move forward' %self.nickname)
     9 
    10     def move_backward(self):
    11         print('%s move backward' %self.nickname)
    12 
    13     def move_left(self):
    14         print('%s move forward' %self.nickname)
    15 
    16     def move_right(self):
    17         print('%s move forward' %self.nickname)
    18 
    19     def attack(self,enemy):
    20         enemy.life_value-=self.aggressivity
    21 class Garen(Hero):
    22     pass
    23 
    24 class Riven(Hero):
    25     pass
    26 
    27 g1=Garen('草丛伦',100,300)
    28 r1=Riven('锐雯雯',57,200)
    29 
    30 print(g1.life_value)
    31 r1.attack(g1)
    32 print(g1.life_value)
    33 
    34 '''
    35 运行结果
    36 '''
    View Code

    提示:用已经有的类建立一个新的类,这样就重用了已经有的软件中的一部分设置大部分,大大生了编程工作量,这就是常说的软件重用,不仅可以重用自己的类,也可以继承别人的,比如标准库,来定制新的数据类型,这就大大缩短了软件开发周期,对大型软件开发来说,意义重大。

    注意:像g1.life_value之类的属性引用,会先从实例中找life_value然后去类中找,然后再去父类中找....直到最顶级的父类。

     1 class Foo:
     2     def f1(self):
     3         print('Foo.f1')
     4 
     5     def f2(self):
     6         print('Foo.f2')
     7         self.f1()
     8 
     9 class Bar(Foo):
    10     def f1(self):
    11         print('Foo.f1')
    12 
    13 
    14 b=Bar()
    15 b.f2()

     九、派生

    子类也可以添加自己新的属性或者在自己这里重新定义这些属性(不会影响到父类),需要注意的是,一旦重新定义了自己的属性且与父类重名,那么调用新增的属性时,就以自己为准了。

    1 class Riven(Hero):
    2     camp='Noxus'
    3     def attack(self,enemy): #在自己这里定义新的attack,不再使用父类的attack,且不会影响父类
    4         print('from riven')
    5     def fly(self): #在自己这里定义新的
    6         print('%s is flying' %self.nickname)

    在子类中,新建的重名的函数属性,在编辑函数内的功能的时候,有可能需要重用父类中重名的那个函数功能,应该是调用普通函数的方式,即:类名:func(),此时就与调用普通函数无异了,因此即便是self测试也要为其传值

     1 class Riven(Hero):
     2     camp='Noxus'
     3     def __init__(self,nickname,aggressivity,life_value,skin):
     4         Hero.__init__(self,nickname,aggressivity,life_value) #调用父类功能
     5         self.skin=skin #新属性
     6     def attack(self,enemy): #在自己这里定义新的attack,不再使用父类的attack,且不会影响父类
     7         Hero.attack(self,enemy) #调用功能
     8         print('from riven')
     9     def fly(self): #在自己这里定义新的
    10         print('%s is flying' %self.nickname)
    11 
    12 r1=Riven('锐雯雯',57,200,'比基尼')
    13 r1.fly()
    14 print(r1.skin)
    15 
    16 '''
    17 运行结果
    18 锐雯雯 is flying
    19 比基尼
    20 
    21 '''

    十、组合与重用性

    软件重用的重要方式除了继承之外还有另外一种方式,即:组合

    组合指的是,在一个类中另外一个类的对象作为数据属性,称为类的组合

     1 >>> class Equip: #武器装备类
     2 ...     def fire(self):
     3 ...         print('release Fire skill')
     4 ... 
     5 >>> class Riven: #英雄Riven的类,一个英雄需要有装备,因而需要组合Equip类
     6 ...     camp='Noxus'
     7 ...     def __init__(self,nickname):
     8 ...         self.nickname=nickname
     9 ...         self.equip=Equip() #用Equip类产生一个装备,赋值给实例的equip属性
    10 ... 
    11 >>> r1=Riven('锐雯雯')
    12 >>> r1.equip.fire() #可以使用组合的类产生的对象所持有的方法
    13 release Fire skill

     组合与继承都是有效地利用已有类的资源的重要方式,但是二者的概念和使用场景皆不同。

    1、继承的方式

    通过继承建立了派生类与基类之间的关系,它是一种‘是’的关系,比如,人是动物。

    当类之间有很多相同的功能,提取这些共同的功能做成基类,用继承比较好比如老师是人,学生是人。

    2、组合的方式

    用组合的方式建立了类与组合的类之间的关系,它是一种‘有’的关系,比如老师教课,老师教python和linux课程,教授有学生s1,s2,s3.....

     1 class OldboyPeople:
     2     school = 'oldboy'
     3     def __init__(self,name,age,sex):
     4         self.name=name
     5         self.age=age
     6         self.sex=sex
     7 
     8     def eat(self):
     9         print('is eating')
    10 
    11 class OldboyStudent(OldboyPeople):
    12     def __init__(self,name,age,sex):
    13         OldboyPeople.__init__(self,name,age,sex)
    14         self.course=[]
    15 
    16     def learn(self):
    17         print('%s is learning'  %self.name)
    18 
    19 class OldboyTeacher(OldboyPeople):
    20     def __init__(self,name,age,sex,salary,title):
    21         OldboyPeople.__init__(self,name,age,sex)
    22         self.salary=salary
    23         self.title=title
    24         self.course=[]
    25 
    26     def teach(self):
    27         print('%s is teaching'  %self.name)
    28 
    29 class Course:
    30     def __init__(self,course_name,course_period,course_price):
    31         self.course_name=course_name
    32         self.course_period=course_period
    33         self.course_price=course_price
    34     def tell_info(self):
    35         print('<课程名:%s 周期:%s 价格:%s>' %(self.course_name,self.course_period,self.course_price))
    36 
    37 python=Course('Python','6mons',3000)
    38 linux=Course('Lnux','3mons',2000)
    39 bigdata=Course('BigData','1mons',1000)
    40 
    41 egon_obj=OldboyTeacher('egon',18,'male',3.1,'沙河霸道金牌讲师')
    42 
    43 egon_obj.course.append(python)
    44 egon_obj.course.append(linux)
    45 
    46 for obj in egon_obj.course:
    47     obj.tell_info()
    48 
    49 yl_obj=OldboyStudent('yanglei',28,'female')
    50 yl_obj.course.append(python)
    51 
    52 for i in yl_obj.course:
    53     # print(i.course_name,i.course_period,i.course_price)
    54     i.tell_info()
    例子:继承与组合

    当类之间有显著不同,并且较小的类是较大的类所需要的组件时,用组合比较好

    十一、继承实现的原理

     1、继承顺序

    新式类广度优先,经典类深度优先

     1 class A(object):
     2     def test(self):
     3         print('from A')
     4 
     5 class B(A):
     6     def test(self):
     7         print('from B')
     8 
     9 class C(A):
    10     def test(self):
    11         print('from C')
    12 
    13 class D(B):
    14     def test(self):
    15         print('from D')
    16 
    17 class E(C):
    18     def test(self):
    19         print('from E')
    20 
    21 class F(D,E):
    22     # def test(self):
    23     #     print('from F')
    24     pass
    25 f1=F()
    26 f1.test()
    27 print(F.__mro__) #只有新式才有这个属性可以查看线性列表,经典类没有这个属性
    28 
    29 #新式类继承顺序:F->D->B->E->C->A
    30 #经典类继承顺序:F->D->B->A->E->C
    31 #python3中统一都是新式类
    32 #pyhon2中才分新式类与经典类
    33 
    34 继承顺序
    继承顺序

    2、继承原理(python如何实现的继承)

    python到底是如何实现继承的,对于定义的每一个类,python会计算出一个解析顺序(MRO)列表,这MRO列表就是一个简单的所有基类的线性顺序列表,

    1 print(F.__mro__) #只有新式才有这个属性可以查看线性列表,经典类没有这个属性
    2 #(<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 #_*_coding:utf-8_*_
     2 __author__ = 'Linhaifeng'
     3 
     4 class Vehicle: #定义交通工具类
     5      Country='China'
     6      def __init__(self,name,speed,load,power):
     7          self.name=name
     8          self.speed=speed
     9          self.load=load
    10          self.power=power
    11 
    12      def run(self):
    13          print('开动啦...')
    14 
    15 class Subway(Vehicle): #地铁
    16     def __init__(self,name,speed,load,power,line):
    17         Vehicle.__init__(self,name,speed,load,power)
    18         self.line=line
    19 
    20     def run(self):
    21         print('地铁%s号线欢迎您' %self.line)
    22         Vehicle.run(self)
    23 
    24 line13=Subway('中国地铁','180m/s','1000人/箱','',13)
    25 line13.run()
    View Code

    方法二:使用内置函数super()

     1 class Vehicle: #定义交通工具类
     2      Country='China'
     3      def __init__(self,name,speed,load,power):
     4          self.name=name
     5          self.speed=speed
     6          self.load=load
     7          self.power=power
     8 
     9      def run(self):
    10          print('开动啦...')
    11 
    12 class Subway(Vehicle): #地铁
    13     def __init__(self,name,speed,load,power,line):
    14         #super(Subway,self) 就相当于实例本身 在python3中super()等同于super(Subway,self)
    15         super().__init__(name,speed,load,power)
    16         self.line=line
    17 
    18     def run(self):
    19         print('地铁%s号线欢迎您' %self.line)
    20         super(Subway,self).run()
    21 
    22 class Mobike(Vehicle):#摩拜单车
    23     pass
    24 
    25 line13=Subway('中国地铁','180m/s','1000人/箱','',13)
    26 line13.run()
    View Code

    强调:二者使用哪一种都可以,但是最好不要混合使用

    即使没有直接继承关系,super仍然会按照mro继续往后查找

     1 #A没有继承B,但是A内super会基于C.mro()继续往后找
     2 class A:
     3     def test(self):
     4         super().test()
     5 class B:
     6     def test(self):
     7         print('from B')
     8 class C(A,B):
     9     pass
    10 
    11 c=C()
    12 c.test() #打印结果:from B
    13 
    14 
    15 print(C.mro())
    16 #[<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>]
    View Code

    指明道姓与super()的区别

     1 #指名道姓
     2 class A:
     3     def __init__(self):
     4         print('A的构造方法')
     5 class B(A):
     6     def __init__(self):
     7         print('B的构造方法')
     8         A.__init__(self)
     9 
    10 
    11 class C(A):
    12     def __init__(self):
    13         print('C的构造方法')
    14         A.__init__(self)
    15 
    16 
    17 class D(B,C):
    18     def __init__(self):
    19         print('D的构造方法')
    20         B.__init__(self)
    21         C.__init__(self)
    22 
    23     pass
    24 f1=D() #A.__init__被重复调用
    25 '''
    26 D的构造方法
    27 B的构造方法
    28 A的构造方法
    29 C的构造方法
    30 A的构造方法
    31 '''
    32 
    33 
    34 #使用super()
    35 class A:
    36     def __init__(self):
    37         print('A的构造方法')
    38 class B(A):
    39     def __init__(self):
    40         print('B的构造方法')
    41         super(B,self).__init__()
    42 
    43 
    44 class C(A):
    45     def __init__(self):
    46         print('C的构造方法')
    47         super(C,self).__init__()
    48 
    49 
    50 class D(B,C):
    51     def __init__(self):
    52         print('D的构造方法')
    53         super(D,self).__init__()
    54 
    55 f1=D() #super()会基于mro列表,往后找
    56 '''
    57 D的构造方法
    58 B的构造方法
    59 C的构造方法
    60 A的构造方法
    61 '''
    View Code

    注意:当使用super()函数时,python会在MRO列表上继续搜索下一个类,只要每个重定义的方法统一使用super()并只调用它一次,那么控制流最终会遍历完整个MRO列表,每个方法也只会被调用一次(注意:使用super调用的所有属性,都是从MRO列表当前的位置往后找,千万不要通过看代码去找继承关系,一定要看MRO列表)

    十三、绑定方法与非绑定方法

    1、类中定义的函数分成俩大类

    1)、绑定方法(绑定给谁,谁来调用就自动将它本身当作第一个参数传入):

        a、绑定到类的方法:用classmethod装饰器装饰的方法。

             为类量身定制

             类.boud_method(),自动将类当作第一个参数传入

             (其实对象也可调用,但仍将类当作第一个参数传入)

         b、绑定到对象的方法:没有被任何装饰器装饰的方法。

             为对象量身定制

             对象.boud_method(),自动将对象当作第一个参数传入

             (属于类的函数,类可以调用,但是必须按照函数的规则来,没有自动传值那么一说)

    2)、非绑定方法:用staticmethod装饰器装饰的方法

             a、不与类或对象绑定,类和对象都可以调用,但是没有自动传值那么一说,就是一个普通工具而已。

             注意:与绑定到对象方法区分开,在类中直接定义的函数,没有被任何装饰器装饰的,都是绑定到对象的方法,可不是普通函数,对象调用该方法会自动传值,而staticmethod装饰的方法,不管谁来调用,都没有自动传值一说。

    2、绑定方法

    绑定给对象的方法(类里面的函数,没有加装饰器的都是给可以绑定给对象)

    绑定给类的方法(slassmethod)

    classmehtod是给类用的,即绑定到类,类在使用时会将类本身当做参数传给类方法的第一个参数(即便是对象来调用也会将类当作第一个参数传入),python为我们内置了函数classmethod来把类中的函数定义成类方法

    1 HOST='127.0.0.1'
    2 PORT=3306
    3 DB_PATH=r'C:UsersAdministratorPycharmProjects	est面向对象编程	est1db'
    View Code
     1 import settings
     2 class MySQL:
     3     def __init__(self,host,port):
     4         self.host=host
     5         self.port=port
     6 
     7     @classmethod
     8     def from_conf(cls):
     9         print(cls)
    10         return cls(settings.HOST,settings.PORT)
    11 
    12 print(MySQL.from_conf) #<bound method MySQL.from_conf of <class '__main__.MySQL'>>
    13 conn=MySQL.from_conf()
    14 
    15 conn.from_conf() #对象也可以调用,但是默认传的第一个参数仍然是类

    3、非绑定方法

    在类内部用staticmethod装饰的函数即非绑定方法,就是普通函数staticmethod不与类或对象绑定,谁都可以调用,没有自动传值效果

     1 import hashlib
     2 import time
     3 class MySQL:
     4     def __init__(self,host,port):
     5         self.id=self.create_id()
     6         self.host=host
     7         self.port=port
     8     @staticmethod
     9     def create_id(): #就是一个普通工具
    10         m=hashlib.md5(str(time.time()).encode('utf-8'))
    11         return m.hexdigest()
    12 
    13 
    14 print(MySQL.create_id) #<function MySQL.create_id at 0x0000000001E6B9D8> #查看结果为普通函数
    15 conn=MySQL('127.0.0.1',3306)
    16 print(conn.create_id) #<function MySQL.create_id at 0x00000000026FB9D8> #查看结果为普通函数

    4、classmethod与staticmethod的区别

     1 import settings
     2 class MySQL:
     3     def __init__(self,host,port):
     4         self.host=host
     5         self.port=port
     6 
     7     @staticmethod
     8     def from_conf():
     9         return MySQL(settings.HOST,settings.PORT)
    10 
    11     # @classmethod #哪个类来调用,就将哪个类当做第一个参数传入
    12     # def from_conf(cls):
    13     #     return cls(settings.HOST,settings.PORT)
    14 
    15     def __str__(self):
    16         return '就不告诉你'
    17 
    18 class Mariadb(MySQL):
    19     def __str__(self):
    20         return '<%s:%s>' %(self.host,self.port)
    21 
    22 
    23 m=Mariadb.from_conf()
    24 print(m) #我们的意图是想触发Mariadb.__str__,但是结果触发了MySQL.__str__的执行,打印就不告诉你:
    25 
    26 mariadb是mysql
    View Code

    5、练习

    定义mysql类

    1 1.对象有id、host、port三个属性
    2 
    3 2.定义工具create_id,在实例化时为每个对象随机生成id,保证id唯一
    4 
    5 3.提供两种实例化方式,方式一:用户传入host和port 方式二:从配置文件中读取host和port进行实例化
    6 
    7 4.为对象定制方法,save和get_obj_by_id,save能自动将对象序列化到文件中,
    8 #文件路径为配置文件中DB_PATH,文件名为id号,保存之前验证对象是否已经存在,若存在则抛出异常,
    9 #;get_obj_by_id方法用来从文件中反序列化出对象
     1 原文链接:http://www.cnblogs.com/dkblog/archive/2011/10/10/2205200.html
     2  Python官方Doc:《20.15. uuid — UUID objects according to RFC 4122 3     UUID的算法介绍:《A Universally Unique IDentifier (UUID) URN Namespace》
     4 
     5 概述:
     6 
     7     UUID是128位的全局唯一标识符,通常由32字节的字符串表示。
     8     它可以保证时间和空间的唯一性,也称为GUID,全称为:
     9             UUID —— Universally Unique IDentifier      Python 中叫 UUID
    10             GUID —— Globally Unique IDentifier          C#  中叫 GUID
    11 
    12     它通过MAC地址、时间戳、命名空间、随机数、伪随机数来保证生成ID的唯一性。
    13     UUID主要有五个算法,也就是五种方法来实现:
    14 
    15        1、uuid1()——基于时间戳
    16 
    17                由MAC地址、当前时间戳、随机数生成。可以保证全球范围内的唯一性,
    18                但MAC的使用同时带来安全性问题,局域网中可以使用IP来代替MAC。
    19 
    20        2、uuid2()——基于分布式计算环境DCE(Python中没有这个函数)
    21 
    22                 算法与uuid1相同,不同的是把时间戳的前4位置换为POSIX的UID。
    23                 实际中很少用到该方法。
    24 
    25       3、uuid3()——基于名字的MD5散列值
    26 
    27                 通过计算名字和命名空间的MD5散列值得到,保证了同一命名空间中不同名字的唯一性,
    28                 和不同命名空间的唯一性,但同一命名空间的同一名字生成相同的uuid。    
    29 
    30        4、uuid4()——基于随机数
    31 
    32                 由伪随机数得到,有一定的重复概率,该概率可以计算出来。
    33 
    34        5、uuid5()——基于名字的SHA-1散列值
    35 
    36                 算法与uuid3相同,不同的是使用 Secure Hash Algorithm 1 算法
    37 
    38 使用方面:
    39 
    40     首先,Python中没有基于DCE的,所以uuid2可以忽略;
    41     其次,uuid4存在概率性重复,由无映射性,最好不用;
    42     再次,若在Global的分布式计算环境下,最好用uuid1;
    43     最后,若有名字的唯一性要求,最好用uuid3或uuid5。
    44 
    45 编码方法:
    46 
    47     # -*- coding: utf-8 -*-
    48 
    49     import uuid
    50 
    51     name = "test_name"
    52     namespace = "test_namespace"
    53 
    54     print uuid.uuid1()  # 带参的方法参见Python Doc
    55     print uuid.uuid3(namespace, name)
    56     print uuid.uuid4()
    57     print uuid.uuid5(namespace, name)
    58 
    59 创建唯一id之UUID
    创建唯一id之UUID
     1 #settings.py内容
     2 '''
     3 HOST='127.0.0.1'
     4 PORT=3306
     5 DB_PATH=r'E:CMSaaadb'
     6 '''
     7 import settings
     8 import uuid
     9 import pickle
    10 import os
    11 class MySQL:
    12     def __init__(self,host,port):
    13         self.id=self.create_id()
    14         self.host=host
    15         self.port=port
    16 
    17     def save(self):
    18         if not self.is_exists:
    19             raise PermissionError('对象已存在')
    20         file_path=r'%s%s%s' %(settings.DB_PATH,os.sep,self.id)
    21         pickle.dump(self,open(file_path,'wb'))
    22 
    23     @property
    24     def is_exists(self):
    25         tag=True
    26         files=os.listdir(settings.DB_PATH)
    27         for file in files:
    28             file_abspath=r'%s%s%s' %(settings.DB_PATH,os.sep,file)
    29             obj=pickle.load(open(file_abspath,'rb'))
    30             if self.host == obj.host and self.port == obj.port:
    31                 tag=False
    32                 break
    33         return tag
    34     @staticmethod
    35     def get_obj_by_id(id):
    36         file_abspath = r'%s%s%s' % (settings.DB_PATH, os.sep, id)
    37         return pickle.load(open(file_abspath,'rb'))
    38 
    39     @staticmethod
    40     def create_id():
    41         return str(uuid.uuid1())
    42 
    43     @classmethod
    44     def from_conf(cls):
    45         print(cls)
    46         return cls(settings.HOST,settings.PORT)
    47 
    48 # print(MySQL.from_conf) #<bound method MySQL.from_conf of <class '__main__.MySQL'>>
    49 conn=MySQL.from_conf()
    50 conn.save()
    51 
    52 conn1=MySQL('127.0.0.1',3306)
    53 conn1.save() #抛出异常PermissionError: 对象已存在
    54 
    55 
    56 obj=MySQL.get_obj_by_id('7e6c5ec0-7e9f-11e7-9acc-408d5c2f84ca')
    57 print(obj.host)
    View Code

    其他练习

     1 class Date:
     2     def __init__(self,year,month,day):
     3         self.year=year
     4         self.month=month
     5         self.day=day
     6     @staticmethod
     7     def now(): #用Date.now()的形式去产生实例,该实例用的是当前时间
     8         t=time.localtime() #获取结构化的时间格式
     9         return Date(t.tm_year,t.tm_mon,t.tm_mday) #新建实例并且返回
    10     @staticmethod
    11     def tomorrow():#用Date.tomorrow()的形式去产生实例,该实例用的是明天的时间
    12         t=time.localtime(time.time()+86400)
    13         return Date(t.tm_year,t.tm_mon,t.tm_mday)
    14 
    15 a=Date('1987',11,27) #自己定义时间
    16 b=Date.now() #采用当前时间
    17 c=Date.tomorrow() #采用明天的时间
    18 
    19 print(a.year,a.month,a.day)
    20 print(b.year,b.month,b.day)
    21 print(c.year,c.month,c.day)
    22 
    23 
    24 #分割线==============================
    25 import time
    26 class Date:
    27     def __init__(self,year,month,day):
    28         self.year=year
    29         self.month=month
    30         self.day=day
    31     @staticmethod
    32     def now():
    33         t=time.localtime()
    34         return Date(t.tm_year,t.tm_mon,t.tm_mday)
    35 
    36 class EuroDate(Date):
    37     def __str__(self):
    38         return 'year:%s month:%s day:%s' %(self.year,self.month,self.day)
    39 
    40 e=EuroDate.now()
    41 print(e) #我们的意图是想触发EuroDate.__str__,但是结果为
    42 '''
    43 输出结果:
    44 <__main__.Date object at 0x1013f9d68>
    45 '''
    46 因为e就是用Date类产生的,所以根本不会触发EuroDate.__str__,解决方法就是用classmethod
    47 
    48 import time
    49 class Date:
    50     def __init__(self,year,month,day):
    51         self.year=year
    52         self.month=month
    53         self.day=day
    54     # @staticmethod
    55     # def now():
    56     #     t=time.localtime()
    57     #     return Date(t.tm_year,t.tm_mon,t.tm_mday)
    58 
    59     @classmethod #改成类方法
    60     def now(cls):
    61         t=time.localtime()
    62         return cls(t.tm_year,t.tm_mon,t.tm_mday) #哪个类来调用,即用哪个类cls来实例化
    63 
    64 class EuroDate(Date):
    65     def __str__(self):
    66         return 'year:%s month:%s day:%s' %(self.year,self.month,self.day)
    67 
    68 e=EuroDate.now()
    69 print(e) #我们的意图是想触发EuroDate.__str__,此时e就是由EuroDate产生的,所以会如我们所愿
    70 '''
    71 输出结果:
    72 year:2017 month:3 day:3
    73 '''
    View Code

            

             

            

  • 相关阅读:
    使用paramikoHelper类实现MySQL安装和数据恢复
    tornado-模板,转义,上传静态文件
    tornado-请求与响应
    tornado-输入
    tornado-输出,request
    配置Ubuntu虚拟环境
    tornado-简单的服务器非阻塞
    Linux查看进程,端口,访问url
    tornado-简单的服务器
    字符串,数组,定时器,form
  • 原文地址:https://www.cnblogs.com/fanglingen/p/7360746.html
Copyright © 2020-2023  润新知