pyday07
一、面向过程与面向对象
1.面向过程的程序设计:
面向过程的程序设计的核心是过程(流水线式思维),过程即解决问题的步骤,面向过程的设计就好比精心设计好一条流水线,考虑周全什么时候处理什么东西。
优点是:极大的降低了程序的复杂度。
缺点是:一套流水线或者流程就是用来解决一个问题,生产汽水的流水线无法生产汽车,即便是能,也得是大改,改一个组件,牵一发而动全身。
应用场景:一旦完成基本很少改变的场景,著名的例子有Linux內核,git,以及Apache HTTP Server等。
2.面向对象的程序设计:
面向对象的程序设计的核心是对象(上帝式思维),要理解对象为何物,必须把自己当成上帝,上帝眼里世间存在的万物皆为对象,不存在的也可以创造出来。
优点是:解决了程序的扩展性。对某一个对象单独修改,会立刻反映到整个体系中,如对游戏中一个人物参数的特征和技能修改都很容易。
缺点:可控性差,无法向面向过程的程序设计流水线式的可以很精准的预测问题的处理流程与结果,面向对象的程序一旦开始就由对象之间的交互解决问题,即便是上帝也无法预测最终结果。于是我们经常看到一个游戏人某一参数的修改极有可能导致阴霸的技能出现,一刀砍死3个人,这个游戏就失去平衡。
应用场景:需求经常变化的软件,一般需求的变化都集中在用户层,互联网应用,企业内部软件,游戏等都是面向对象的程序设计大显身手的好地方
面向对象的程序设计并不是全部。对于一个软件质量来说,面向对象的程序设计只是用来解决扩展性。

类的数据属性,类的函数属性
定义在类内部的变量(数据属性),是所有对象共有的,id全一样
定义在类内部的函数,是绑定到每个对象的,是给对象来用的,id不同,如p1.name('elaine')会把默认把p1本身当做第一个参数传入,在定义类时也一样,p1 = chinese('elaine',18)相当于把p1作为第一个参数传入__init__
二、面向对象:类和对象
1.python中一切皆为对象,且python3统一了类与类型的概念,类型就是类,如下:
>>> dict #类型dict就是类dict <class 'dict'> >>> d=dict(name='dylan') #实例化 >>> d.pop('name') #向d发一条消息,执行d的方法pop 'dylan'
从一组对象中提取相似的部分就是类,类所有对象都具有的特征和技能的结合体
在python中,用变量表示特征,用函数表示技能,因而类是变量与函数的结合体,对象是变量与方法(指向类的函数)的结合体
2.类:
在python中声明函数与声明类很相似
def functionName(args): '函数文档字符串' 函数体
''' class 类名: '类的文档字符串' 类体 ''' #我们创建一个类 class Data: pass
3.类有两种作用:属性引用和实例化:
class Blademaster: #定义英雄剑圣的类,不同的玩家可以用它实例出自己英雄; race='Orc' #所有玩家的英雄(剑圣)的种族都是Orc; def attack(self,enemy): #普通攻击技能,enemy是敌人; enemy.life_value-=self.aggressivity #根据自己的攻击力,攻击敌人就减掉敌人的生命值。
属性引用(类名.属性名):
>>> Blademaster.race #引用类的数据属性,该属性与所有对象/实例共享 'Orc' >>> Blademaster.attack #引用类的函数属性,该属性也共享 <function Blademaster.attack at 0x101356510> >>> Blademaster.name='Hellscream' #增加属性 >>> del Blademaster.name #删除属性
实例化:
类名加括号就是实例化,会自动触发__init__函数的运行,可以用它来为每个实例定制自己的特征
class Blademaster: #定义英雄剑圣的类,不同的玩家可以用它实例出自己英雄; race='Orc' #所有玩家的英雄(剑圣)的阵营都是Orc; def __init__(self,nickname,aggressivity=58,life_value=455): #英雄的初始攻击力58...; self.nickname=nickname #为自己的剑圣起个别名; self.aggressivity=aggressivity #英雄都有自己的攻击力; self.life_value=life_value #英雄都有自己的生命值; def attack(self,enemy): #普通攻击技能,enemy是敌人; enemy.life_value-=self.aggressivity #根据自己的攻击力,攻击敌人就减掉敌人的生命值。
def scream(self):
print('For the horde!')
b1=Blademaster('Hellscream') #就是在执行Blademaster.__init__(b1,'Hellscream'),然后执行__init__内的代码b1.nickname='Hellscream'等
self的作用是在实例化时自动将对象/实例本身传给__init__的第一个参数,self可以是任意名字
这种自动传递的机制还体现在b1.attack的调用上
补充:
1)我们定义的类的属性到底存到哪里了?有两种方式查看
dir(类名):查出的是一个名字列表
类名.__dict__:查出的是一个字典,key为属性名,value为属性值
print(dir(Blademaster)) print(Blademaster.__dict__) 运行结果: ['__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__', 'attack', 'race'] {'__init__': <function Blademaster.__init__ at 0x0000000000B7E268>, 'attack': <function Blademaster.attack at 0x0000000000B7E2F0>, '__module__': '__main__', '__weakref__': <attribute '__weakref__' of 'Blademaster' objects>, '__dict__': <attribute '__dict__' of 'Blademaster' objects>, 'race': 'Orc', '__doc__': None}
2)特殊的类属性:
类名.__name__# 类的名字(字符串) 类名.__doc__# 类的文档字符串 类名.__base__# 类的第一个父类(在讲继承时会讲) 类名.__bases__# 类所有父类构成的元组(在讲继承时会讲) 类名.__dict__# 类的字典属性 类名.__module__# 类定义所在的模块 类名.__class__# 实例对应的类(仅新式类中)
4.对象相关:
对象是关于类而实际存在的一个例子,即实例
b1=Blademaster('Hellscream') #类实例化得到b1这个实例 print(type(b1)) #查看b1的类型就是类Blademaster print(isinstance(b1,Blademaster)) #b1就是Blademaster的实例 运行结果: <class '__main__.Blademaster'> True
对象/实例只有一种作用:属性引用
#对象/实例本身其实只有数据属性 print(b1.nickname) print(b1.aggressivity) print(b1.life_value) #查看实例属性同样是dir和内置__dict__两种方式 运行结果: Hellscream 58 455
对象/实例本身只有数据属性,但是python的class机制会将类的函数绑定到对象上,称为对象的方法,或者叫绑定方法,绑定方法唯一绑定一个对象,同一个类的方法绑定到不同的对象上,属于不同的方法,内存地址都不会一样
b1=Blademaster('Grom·Hellscream') #类实例化得到b1这个实例 b2=Blademaster('Ogrim·Doomhammer') #类实例化得到b2这个实例 print(id(b1.race)) #2个不同对象引用类的数据属性,该属性内存地址的相同的 print(id(b2.race)) print(b1.attack) #不同对象引用类的函数属性,各自绑定,内存地址不同 print(b2.attack) 运行结果: 16895648 16895648 <bound method Blademaster.attack of <__main__.Blademaster object at 0x000000000102F160>> <bound method Blademaster.attack of <__main__.Blademaster object at 0x000000000102F198>>
对象的绑定方法的特别之处在于:obj.func()会把obj传给func的第一个参数。
b1.scream()
b2.scream()
运行结果:
For the horde!
For the horde!
5.对象之间交互:
可以模仿Blademaster再创建一个DemonHunter类
class Demonhunter: # 定义英雄恶魔猎手的类,不同的玩家可以用它实例出自己英雄; race = 'Night Elf' # 所有玩家的英雄(恶魔猎手)的阵营都是Night Elf; def __init__(self, nickname, aggressivity=60, life_value=425): # 英雄的初始攻击力58...; self.nickname = nickname # 为自己的恶魔猎手起个别名; self.aggressivity = aggressivity # 英雄都有自己的攻击力; self.life_value = life_value # 英雄都有自己的生命值; def attack(self, enemy): # 普通攻击技能,enemy是敌人; enemy.life_value -= self.aggressivity # 根据自己的攻击力,攻击敌人就减掉敌人的生命值。 def talk(self): print('I`m blind not deaf!')
实例出一个Illidan·Stormrage
d1 = Demonhunter('Illidan·Stormrage')
交互:Illidan·Stormrage攻击Grom·Hellscream
d1.attack(b1) print(b1.life_value) b1.attack(d1) print(d1.life_value) 运行结果: 395 367
6.类名称空间与对象名称空间:
创建一个类就会创建一个类的名称空间,用来存储类中定义的所有名字,这些名字称为类的属性
而类有两种属性:数据属性和函数属性
其中类的数据属性是共享给所有对象的,内存地址相同
print(id(b1.race)) #本质就是在引用类的race属性,二者id一样 print(id(b2.race)) 运行结果: 17047600 17047600
而类的函数属性是绑定给各个对象的,内存地址不同
print(b1.scream) print(b2.scream) 运行结果: <bound method Blademaster.scream of <__main__.Blademaster object at 0x0000000001092320>> <bound method Blademaster.scream of <__main__.Blademaster object at 0x0000000001092358>> ''' b1.scream就是在执行Blademaster.scream的功能,python的class机制会将Blademaster类的函数属性scream绑定给b1,b1相当于拿到了一个指针,指向Blademaster类的scream功能 除此之外b1.scream()会将b1传给scream的第一个参数 '''
创建一个对象就会创建一个对象的名称空间,存放对象的名字,称为对象的属性
在obj.name会先从obj自己的名称空间里找name,找不到则去类中找,类也找不到就找父类,最后都找不到就抛出异常
补充:关于is 和 ==
# is判断的是内存地址,==判断的是值 >>> x = 1 >>> y = 1 >>> x == y True >>> x is y True >>> a = 300 # 数值较大时会开辟新的内存空间 >>> b = 300 >>> a == b True >>> a is b False
三、类的继承
1.什么是继承:
继承是一种创建新类的方式,在python中,新建的类可以继承一个或多个父类,父类又可称为基类或超类,新建的类称为派生类或子类
python中类的继承分为:单继承和多继承
class ParentClass1: #定义父类 pass class ParentClass2: #定义父类 pass class SubClass1(ParentClass1): #单继承,基类是ParentClass1,派生类是SubClass pass class SubClass2(ParentClass1,ParentClass2): #python支持多继承,用逗号分隔开多个继承的类 pass
查看继承
>>> SubClass1.__bases__ #__base__只查看从左到右继承的第一个子类,__bases__则是查看所有继承的父类 (<class '__main__.ParentClass1'>,) >>> SubClass2.__bases__ (<class '__main__.ParentClass1'>, <class '__main__.ParentClass2'>)
提示:如果没有指定基类,python的类会默认继承object类,object是所有python类的基类,它提供了一些常见方法(如__str__)的实现
>>> ParentClass1.__bases__ (<class 'object'>,) >>> ParentClass2.__bases__ (<class 'object'>,)
2.继承与重用性:
针对上面的问题,我们发现'剑圣'类和'恶魔猎手'类有很多重复的代码,我们可以定义一个公用的'Hero_unit'类,让这两个类继承'Hero_unit'类(这两个类会继承所有属性:数据属性和函数属性),而针对不同的英雄单位,会有不同的种族和语音台词,我们可以在其类下定义不同的数据属性和函数属性实现,
class Hero_unit: def __init__(self, nickname, aggressivity, life_value): self.nickname = nickname self.aggressivity = aggressivity self.life_value = life_value def attack(self, enemy): enemy.life_value -= self.aggressivity class Blademaster(Hero_unit): race='Orc' def battle_scream(self): print('For the horde!') class Demonhunter(Hero_unit): race = 'Night Elf' def talk(self): print('I`m blind not deaf!') class Deathknight(Hero_unit): race = 'Undead' def talk(self): print('Frostmourn hungers!')
如果新建一个'死亡骑士'类,同样也可以继承'Hero_unit'类属性,当我们想给新建的'死亡骑士'对象添加一个护甲值的属性时,而同样这个'死亡骑士'对象也要具有其他'Hero_unit'对象所具有的属性,我们可以用如下的方式继承:
class Deathknight(Hero_unit): race = 'Undead' def __init__(self,nickname, aggressivity, life_value,armor_value): Hero_unit.__init__(self, nickname, aggressivity, life_value) #调用父类对象属性 self.armor_value = armor_value # 新的对象属性 def talk(self): # 这里定义的talk属性与父类的不会冲突 Hero_unit.talk(self) # 父类的talk依旧可以调用 print('Frostmourn hungers!') d1 = Deathknight('Arthas·Menethil',45,60,5) d1.talk()
运行结果:
Your Hero:
Frostmourn hungers!
3.组合与重用性:
重用的方式除了继承之外还有另外一种方式,即:组合
组合指的是,在一个类中以另外一个类的对象作为数据属性,称为类的组合
比如要给'死亡骑士'类的对象定义一个属性:武器
代码如下:
class Hero_unit(): def __init__(self, nickname, aggressivity, life_value): self.nickname = nickname self.aggressivity = aggressivity self.life_value = life_value def attack(self, enemy): enemy.life_value -= self.aggressivity def talk(self): print('Your Hero:') class Weapon: # Weapon类 def __init__(self,weapon_name): self.weapon_name = weapon_name def equip(self): # 产生武器对象后调用此方法装备武器 print('Hero equip with %s'%self.weapon_name) class Deathknight(Hero_unit): #英雄'Deathknight',要'有'武器,因而组合'Weapon'类 race = 'Undead' def __init__(self,nickname, aggressivity, life_value,armor_value,weapon_name): Hero_unit.__init__(self, nickname, aggressivity, life_value) self.armor_value = armor_value self.equip_weapon = Weapon(weapon_name).equip() #用Weapon类产生一个武器对象,并调用equip方法,赋值给对象数据属性 def talk(self): Hero_unit.talk(self) print('Frostmourn hungers!') d1 = Deathknight('Arthas·Menethil',45,60,5,'Frostmourn') #输入英雄的基本属性,并传入武器:霜之哀伤 生成对象 运行结果: Hero equip with Frostmourn
4.继承与组合的区别:
组合与继承都是有效地利用已有类的资源的重要方式。但是二者的概念和使用场景皆不同
1)继承的方式:
通过继承建立了派生类与基类之间的关系,它是一种'是'的关系,比如'死亡骑士'是'英雄单位','剑圣'也是'英雄单位'。当类之间有很多相同的功能,提取这些共同的功能做成基类,用继承比较好。
2)组合的方式:
用组合的方式建立了类与组合的类之间的关系,它是一种‘有’的关系,比如'死亡骑士'有武器'霜之哀伤'
5.接口与归一化设计:
6.抽象类:
1)什么是抽象类
与java一样,python也有抽象类的概念但是同样需要借助模块实现,抽象类是一个特殊的类,它的特殊之处在于只能被继承,不能被实例化
2)为什么要有抽象类
如果说类是从一堆对象中抽取相同的内容而来的,那么抽象类就是从一堆类中抽取相同的内容而来的,内容包括数据属性和函数属性。
比如我们有香蕉的类,有苹果的类,有桃子的类,从这些类抽取相同的内容就是水果这个抽象的类,你吃水果时,要么是吃一个具体的香蕉,要么是吃一个具体的桃子。。。。。。你永远无法吃到一个叫做水果的东西。
从设计角度去看,如果类是从现实对象抽象而来的,那么抽象类就是基于类抽象而来的。
从实现角度来看,抽象类与普通类的不同之处在于:抽象类中只能有抽象方法(没有实现功能),该类不能被实例化,只能被继承,且子类必须实现抽象方法。这一点与接口有点类似,但其实是不同的,即将揭晓答案
3)在python中实现抽象类
#_*_coding:utf-8_*_ __author__ = 'Linhaifeng' #一切皆文件 import abc #利用abc模块实现抽象类 class All_file(metaclass=abc.ABCMeta): all_type='file' @abc.abstractmethod #定义抽象方法,无需实现功能 def read(self): '子类必须定义读功能' pass @abc.abstractmethod #定义抽象方法,无需实现功能 def write(self): '子类必须定义写功能' pass # class Txt(All_file): # pass # # t1=Txt() #报错,子类没有定义抽象方法 class Txt(All_file): #子类继承抽象类,但是必须定义read和write方法 def read(self): print('文本数据的读取方法') def write(self): print('文本数据的读取方法') class Sata(All_file): #子类继承抽象类,但是必须定义read和write方法 def read(self): print('硬盘数据的读取方法') def write(self): print('硬盘数据的读取方法') class Process(All_file): #子类继承抽象类,但是必须定义read和write方法 def read(self): print('进程数据的读取方法') def write(self): print('进程数据的读取方法') wenbenwenjian=Txt() yingpanwenjian=Sata() jinchengwenjian=Process() #这样大家都是被归一化了,也就是一切皆文件的思想 wenbenwenjian.read() yingpanwenjian.write() jinchengwenjian.read() print(wenbenwenjian.all_type) print(yingpanwenjian.all_type) print(jinchengwenjian.all_type)
4)抽象类与接口:
抽象类的本质还是类,指的是一组类的相似性,包括数据属性(如all_type)和函数属性(如read、write),而接口只强调函数属性的相似性。
抽象类是一个介于类和接口直接的一个概念,同时具备类和接口的部分特性,可以用来实现归一化设计
7.继承的实现原理(继承顺序):
1)继承顺序:

2)继承原理:
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.如果对下一个类存在两个合法的选择,选择第一个父类
3)调用父类的方法:
方法一:父类名.父类方法()
class Hero_unit(): def __init__(self, nickname, aggressivity, life_value): self.nickname = nickname self.aggressivity = aggressivity self.life_value = life_value def attack(self, enemy): enemy.life_value -= self.aggressivity def talk(self): print('Your Hero:') class Deathknight(Hero_unit): race = 'Undead' def __init__(self,nickname, aggressivity, life_value,armor_value): Hero_unit.__init__(self, nickname, aggressivity, life_value) #调用父类__init__方法 self.armor_value = armor_value def talk(self): Hero_unit.talk(self) # 调用父类的talk方法 print('Frostmourn hungers!') d1 = Deathknight('Arthas·Menethil',45,60,5) d1.talk() 运行结果: Your Hero: Frostmourn hungers!
方法二:super().父类方法()
class Deathknight(Hero_unit): race = 'Undead' def __init__(self,nickname, aggressivity, life_value,armor_value): super().__init__(nickname, aggressivity, life_value) self.armor_value = armor_value def talk(self): super().talk() print('Frostmourn hungers!') d1 = Deathknight('Arthas·Menethil',45,60,5) d1.talk() 运行结果:
Your Hero:
Frostmourn hungers!
当你使用super()函数时,Python会在MRO列表上继续搜索下一个类。只要每个重定义的方法统一使用super()并只调用它一次,那么控制流最终会遍历完整个MRO列表,每个方法也只会被调用一次
注意:使用super调用的所有属性,都是从MRO列表当前的位置往后找,千万不要通过看代码去找继承关系,一定要看MRO列表
#每个类中都继承了且重写了父类的方法 class A: def __init__(self): print('A的构造方法') class B(A): def __init__(self): print('B的构造方法') A.__init__(self) class C(A): def __init__(self): print('C的构造方法') A.__init__(self) class D(B,C): def __init__(self): print('D的构造方法') B.__init__(self) C.__init__(self) pass f1=D() print(D.__mro__) #python2中没有这个属性 运行结果: D的构造方法 B的构造方法 A的构造方法 C的构造方法 A的构造方法 (<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)
# 每个类中都继承了且重写了父类的方法 class A: def __init__(self): print('A的构造方法') class B(A): def __init__(self): print('B的构造方法') super(B,self).__init__() # python2的super用法,python3直接super() class C(A): def __init__(self): print('C的构造方法') super(C,self).__init__() class D(B,C): def __init__(self): print('D的构造方法') super(D,self).__init__() f1=D() print(D.__mro__) #python2中没有这个属性 运行结果: D的构造方法 B的构造方法 C的构造方法 A的构造方法 (<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)
四、多态与多态性
1.多态指的是一类事物有多种形态,(一个类有多个子类,因而多态的概念依赖于继承)
正如上面的列子:
Hero_unit有Blademaster,Demonhunter,DeathKnight三个类;Blademaster,Demonhunter,DeathKnight就是Hero_nuit的三种形态
2.多态性(多态与多态性是两种概念):
多态性是指具有不同功能的函数可以使用相同的函数名,这样就可以用一个函数名调用不同功能的函数。
正如Blademaster,Demonhunter,DeathKnight类都可以有显示语音台词talk方法,但是他们显示的语音台词又不同,这样用同一个talk方法就实现了不同功能
class Hero_unit(): def __init__(self, nickname, aggressivity, life_value): self.nickname = nickname self.aggressivity = aggressivity self.life_value = life_value def attack(self, enemy): enemy.life_value -= self.aggressivity def talk(self): print('Your Hero:') class Blademaster(Hero_unit): race='Orc' def talk(self): print('For the horde!') class Demonhunter(Hero_unit): race = 'Night Elf' def talk(self): print('I`m blind not deaf!') class Deathknight(Hero_unit): race = 'Undead' def talk(self): print('Frostmourn hungers!') #多态: blademaster1 = Blademaster('Grom·Hellscream',58,455) demonhunter1 = Demonhunter('Illidan·Stormrage',60,425) deathknight1 = Deathknight('Arthas·Menethil',45,600)
#多态性: def talk_all_unit(obj): obj.talk() talk_all_unit(blademaster1) talk_all_unit(demonhunter1) talk_all_unit(deathknight1)
运行结果:
For the horde!
I`m blind not deaf!
Frostmourn hungers!
如果我们新生成一个Paladin类,由Paladin类产生的实例paladin1,使用者可以在完全不需要修改自己代码的情况下,使用和Blademaster,Demonhunter,Deathknight一样的talk方法:
class Paladin(Hero_unit): race = 'Human' def talk(self): print('Justice will be served!') paladin1 = Paladin('Lightbringer·Uther',42,610) talk_all_unit(paladin1) 运行结果: For the horde! I`m blind not deaf! Frostmourn hungers! Justice will be served!
五、封装
封装其实分为两个层面,但无论哪种层面的封装,都要对外界提供好访问你内部隐藏内容的接口(接口可以理解为入口,有了这个入口,使用者无需且不能够直接访问到内部隐藏的细节,只能走接口,并且我们可以在接口的实现上附加更多的处理逻辑,从而严格控制使用者的访问)
1.第一种层面的封装:
创建类和对象会分别创建二者的名称空间,我们只能用类名.或者obj.的方式去访问里面的名字,这本身就是一种封装
blademaster1 = Blademaster('Grom·Hellscream',58,455) print(blademaster1.nickname) print(Blademaster.race) 运行结果: Grom·Hellscream Orc
对于这一层面的封装(隐藏),类名.和实例名.就是访问隐藏属性的接口
2.第二种层面的封装:
类中把某些属性和方法隐藏起来(或者说定义成私有的),只在类的内部使用、外部无法访问,或者留下少量接口(函数)供外部访问。
在python中用双下划线的方式实现隐藏属性(设置成私有的)
类中所有双下划线开头的名称如__x都会自动变形成:_类名__x的形式:
class A: __N=0 #类的数据属性就应该是共享的,但是语法上是可以把类的数据属性设置成私有的如__N,会变形为_A__N def __init__(self): self.__X=10 #变形为self._A__X def __foo(self): #变形为_A__foo print('from A') def bar(self): self.__foo() #只有在类内部才可以通过__foo的形式访问到. a1 = A() # print(A.__N) #私有属性无法直接访问 print(A.__dict__) print(a1.__dict__)
print(A._A__N) print(a1._A__X) a1._A__foo() a1.bar() 运行结果: {'__init__': <function A.__init__ at 0x0000000000B9E268>, 'bar': <function A.bar at 0x0000000000B9E378>, '__weakref__': <attribute '__weakref__' of 'A' objects>, '__dict__': <attribute '__dict__' of 'A' objects>, '__module__': '__main__', '_A__N': 0, '__doc__': None, '_A__foo': <function A.__foo at 0x0000000000B9E2F0>} {'_A__X':10}
0 10 from A from A
这种自动变形的特点:
1.类中定义的__x只能在内部使用,如self.__x,引用的就是变形的结果。
2.这种变形其实正是针对外部的变形,在外部是无法通过__x这个名字访问到的。
3.在子类定义的__x不会覆盖在父类定义的__x,因为子类中变形成了:_子类名__x,而父类中变形成了:_父类名__x,即双下滑线开头的属性在继承给子类时,子类是无法覆盖的。
class Parent: def foo(self): print('from Parent.foo') self.bar() def bar(self): print('from Parent.bar') class Son(Parent): def bar(self): print('from Son.bar') s = Son() # s.bar() s.foo() 运行结果: from Parent.foo from Son.bar class Parent: def foo(self): print('from Parent.foo') self.__bar() def __bar(self): print('from Parent.bar') class Son(Parent): def bar(self): print('from Son.bar') s = Son() # s.bar() s.foo() 运行结果: from Parent.foo from Parent.bar class Parent: def foo(self): print('from Parent.foo') self.__bar() def __bar(self): # _Parent__bar print('from Parent.bar') class Son(Parent): def __bar(self): # _Son__bar print('from Son.bar') s = Son() # s.bar() s.foo() 运行结果: from Parent.foo from Parent.bar
注意:对于这一层面的封装(隐藏),我们需要在类中定义一个函数(接口函数)在它内部访问被隐藏的属性,然后外部就可以使用了,如下:
class Hide_hero: __race = 'pandaren' def __init__(self,nickname, aggressivity=46, life_value=595): self.__nickname = nickname self.__aggressivity = aggressivity self.__life_value = life_value def show_hero(self): # 定义访问隐藏英雄属性的借口函数 print('Hero_name:%s Race:%s Agg:%s Lv:%s'%(self.__nickname, self.__race, self.__aggressivity, self.__life_value)) p1 = Hide_hero('Chen·Stormstout') p1.show_hero() 运行结果: Hero_name:Chen·Stormstout Race:pandaren Agg:46 Lv:595
变形的过程只在类的定义是发生一次,在定义后的赋值操作,不会变形
p1 = Hide_hero('Chen·Stormstout') p1.show_hero() print(p1.__dict__) p1.__skill = 'Drink Beer' print(p1.__dict__) print(Hide_hero.__dict__) Hide_hero.__area = 'Pandaria' print(Hide_hero.__dict__) 运行结果: # 定义后添加的__skill 和 __area并没有变形 Hero_name:Chen·Stormstout Race:pandaren Agg:46 Lv:595 {'_Hide_hero__life_value': 595, '_Hide_hero__nickname': 'Chen·Stormstout', '_Hide_hero__aggressivity': 46} {'_Hide_hero__life_value': 595, '__skill': 'Drink Beer', '_Hide_hero__nickname': 'Chen·Stormstout', '_Hide_hero__aggressivity': 46} {'__dict__': <attribute '__dict__' of 'Hide_hero' objects>, '_Hide_hero__race': 'pandaren', 'show_hero': <function Hide_hero.show_hero at 0x0000000000A0E2F0>, '__doc__': None, '__module__': '__main__', '__weakref__': <attribute '__weakref__' of 'Hide_hero' objects>, '__init__': <function Hide_hero.__init__ at 0x0000000000A0E268>} {'__dict__': <attribute '__dict__' of 'Hide_hero' objects>, '_Hide_hero__race': 'pandaren', 'show_hero': <function Hide_hero.show_hero at 0x0000000000A0E2F0>, '__doc__': None, '__module__': '__main__', '__weakref__': <attribute '__weakref__' of 'Hide_hero' objects>, '__init__': <function Hide_hero.__init__ at 0x0000000000A0E268>, '__area': 'Pandaria'}
在继承中,父类如果不想让子类覆盖自己的方法,可以将方法定义为私有的
# 子类调用foo方法去查找父类,父类的foo调用bar方法时却调用了子类的bar class Parent: def foo(self): print('from Parent.foo') self.bar() def bar(self): print('from Parent.bar') class Son(Parent): def bar(self): print('from Son.bar') s = Son() # s.bar() s.foo() 运行结果: from Parent.foo from Son.bar
#将父类的bar定义为私有方法,这次再调用foo先去找父类的foo,而父类调用__bar时相当于再找自己变形的_Parent__bar,而不是子类的__bar即_Son__bar class Parent: def foo(self): print('from Parent.foo') self.__bar() def __bar(self): print('from Parent.bar') class Son(Parent): def __bar(self): print('from Son.bar') s = Son() # s.bar() s.foo() 运行结果: from Parent.foo from Parent.bar
3.特性(property):
property是一种特殊的属性,访问它时会执行一段功能(函数)然后返回值
#例一:BMI指数(bmi是计算而来的,但很明显它听起来像是一个属性而非方法,如果我们将其做成一个属性,更便于理解) ''' 成人的BMI数值: 过轻:低于18.5 正常:18.5-23.9 过重:24-27 肥胖:28-32 非常肥胖, 高于32 体质指数(BMI)=体重(kg)÷身高^2(m) EX:70kg÷(1.75×1.75)=22.86 '''
class People: def __init__(self,name,weight,height): self.name = name self.weight = weight self.height = height @property def Bmi(self): return self.weight / (self.height**2) p1 = People('dylan',81,1.82) print(p1.Bmi) 运行结果: 24.453568409612362
#例二:圆的周长和面积 import math class Circle: def __init__(self,radius): #圆的半径radius self.radius=radius @property def area(self): return math.pi * self.radius**2 #计算面积 @property def perimeter(self): return 2*math.pi*self.radius #计算周长 c=Circle(10) print(c.radius) print(c.area) #可以向访问数据属性一样去访问area,会触发一个函数的执行,动态计算出一个值 print(c.perimeter) #同上 ''' 输出结果: 314.1592653589793 62.83185307179586 '''
注意:此时的特性arear和perimeter不能被赋值
c.area=3 #为特性area赋值 ''' 抛出异常: AttributeError: can't set attribute '''
前面针对私有属性想对其进行修改和删除,我们一般会进行如下的操作,在私有属性的类里定义一个方法,在方法内对其进行更改和删除操作,然后外面调用这个方法,就可以对这些对象的私有属性进行操作了:
如之前定义的隐藏英雄熊猫人,想对其进行名字的更改或者删除操作:
class Hide_hero: __race = 'pandaren' def __init__(self,nickname, aggressivity=46, life_value=595): self.__nickname = nickname self.__aggressivity = aggressivity self.__life_value = life_value def show_hero(self): # 定义访问隐藏英雄属性的借口函数 print('Hero_name:%s Race:%s Agg:%s Lv:%s'%(self.__nickname, self.__race, self.__aggressivity, self.__life_value)) def set_name(self,new_name): # 修改英雄名字的接口函数 self.__nickname = new_name def del_name(self): # 删除英雄名字的接口函数 del self.__nickname p1 = Hide_hero('Chen·Stormstout') p1.show_hero() p1.set_name('Lorewalker·Cho') p1.show_hero() p1.del_name() p1.show_hero() 运行结果: Hero_name:Chen·Stormstout Race:pandaren Agg:46 Lv:595 Hero_name:Lorewalker·Cho Race:pandaren Agg:46 Lv:595 AttributeError: 'Hide_hero' object has no attribute '_Hide_hero__nickname' # 名字已经删除,故报错不存在
我们也可以利用property实现,这样使其看起来更像对象的一个属性:
class Hide_hero: __race = 'pandaren' def __init__(self,nickname, aggressivity=46, life_value=595): self.__nickname = nickname self.__aggressivity = aggressivity self.__life_value = life_value @property def nickname(self): # 定义访问隐藏英雄属性的借口函数 return self.__nickname @nickname.setter def nickname(self,new_name): # 修改英雄名字的接口函数 self.__nickname = new_name @nickname.deleter def nickname(self): # 删除英雄名字的接口函数 del self.__nickname p1 = Hide_hero('Chen·Stormstout') print(p1.nickname) p1.nickname = 'Lorewalker·Cho' print(p1.nickname) del p1.nickname print(p1.nickname) 运行结果: Chen·Stormstout Lorewalker·Cho AttributeError: 'Hide_hero' object has no attribute '_Hide_hero__nickname'
property这样用看起来更容易理解一些:
class Hide_hero: __race = 'pandaren' def __init__(self,nickname, aggressivity=46, life_value=595): self.__nickname = nickname self.__aggressivity = aggressivity self.__life_value = life_value def show_hero(self): # 定义访问隐藏英雄属性的借口函数 return ('Hero_name:%s Race:%s Agg:%s Lv:%s'%(self.__nickname, self.__race, self.__aggressivity, self.__life_value)) def set_name(self,new_name): # 修改英雄名字的接口函数 self.__nickname = new_name def del_name(self): # 删除英雄名字的接口函数 del self.__nickname nickname = property(show_hero,set_name,del_name) p1 = Hide_hero('Chen·Stormstout') print(p1.nickname) p1.nickname = 'Lorewalker·Cho' print(p1.nickname) del p1.nickname print(p1.nickname)
七、绑定方法和非绑定方法
类中定义的函数分成两大类:
一:绑定方法(绑定给谁,谁来调用就自动将它本身当作第一个参数传入):
1. 绑定到类的方法:用classmethod装饰器装饰的方法。
为类量身定制
类.boud_method(),自动将类当作第一个参数传入
(其实对象也可调用,但仍将类当作第一个参数传入)
2. 绑定到对象的方法:没有被任何装饰器装饰的方法。
为对象量身定制
对象.boud_method(),自动将对象当作第一个参数传入
(属于类的函数,类可以调用,但是必须按照函数的规则来,没有自动传值那么一说)
二:非绑定方法:用staticmethod装饰器装饰的方法
1. 不与类或对象绑定,类和对象都可以调用,但是没有自动传值那么一说。就是一个普通工具而已
注意:与绑定到对象方法区分开,在类中直接定义的函数,没有被任何装饰器装饰的,都是绑定到对象的方法,可不是普通函数,对象调用该方法会自动传值,而staticmethod装饰的方法,不管谁来调用,都没有自动传值一说
1.非绑定方法( staticmethod):
statimethod不与类或对象绑定,谁都可以调用,没有自动传值效果,python为我们内置了函数staticmethod来把类中的函数定义成静态方法
例如为了避免重复,我们想给每一个创建的英雄单位都定义一个id标示
class Hero_unit(): def __init__(self, nickname, aggressivity, life_value): self.nickname = nickname self.aggressivity = aggressivity self.life_value = life_value def attack(self, enemy): enemy.life_value -= self.aggressivity def talk(self): print('Your Hero:') @staticmethod def creat_id(): # 这里creat_id就作为一个工具函数,谁都可以调用 m = hashlib.md5(str(time.clock()).encode('utf-8')) # time.clock为cpu时间,每次执行都不一样,所以每次hash的结果也不同,可以作为唯一标示(time.time在同时执行时会产生同一个时间戳) return m.hexdigest() h1 =Hero_unit('Blademaster',52,445) print(h1.creat_id()) print(Hero_unit.creat_id()) 运行结果: 8eb5f6a994799c5c8a976e6194ad2a0f 3096e4119e939c29302ad86ad3d84d30
2.类绑定方法(classmethod):
classmehtod是给类用的,即绑定到类,类在使用时会将类本身当做参数传给类方法的第一个参数(对象也可以调用,但即便是对象来调用也会将类当作第一个参数传入),python为我们内置了函数classmethod来把类中的函数定义成类方法
例如,我们现在想创建一个随机的英雄对象:
class Hero_unit(): def __init__(self, nickname, aggressivity, life_value): self.nickname = nickname self.aggressivity = aggressivity self.life_value = life_value def attack(self, enemy): enemy.life_value -= self.aggressivity def talk(self): print('Your Hero:') @staticmethod def creat_id(): m = hashlib.md5(str(time.clock()).encode('utf-8')) # time.clock为cpu时间,每次执行都不一样,所以每次hash的结果也不同,可以作为唯一标示(time.time在同时执行时会产生同一个时间戳) return m.hexdigest() @classmethod def random_hero(cls): print(cls) name_list = ['Chen·Stormstout', 'Lorewalker·Cho', ] random_name = random.choice(name_list) random_agg = random.randint(45,65) random_life_value = random.randint(395,650) return cls(random_name,random_agg,random_life_value) print(Hero_unit.random_hero) print(Hero_unit.random_hero) h1 = Hero_unit.random_hero() h2 = Hero_unit.random_hero() print(h1.nickname,h1.aggressivity,h1.life_value) print(h2.nickname,h2.aggressivity,h2.life_value) 运行结果: <bound method type.random_hero of <class '__main__.Hero_unit'>> <bound method type.random_hero of <class '__main__.Hero_unit'>> <class '__main__.Hero_unit'> <class '__main__.Hero_unit'> Lorewalker·Cho 62 610 Chen·Stormstout 55 479
3.staticmethod和classmethod的区别:
现在我们都是从Hero_unit里产生的随机英雄对象,现在我们新定义一个剑圣类,继承Hero_unit,让新的随机英雄对象都是从剑圣类里产生的,
首先我们使用 staticmethod 去定义这个随机产生英雄的方法:
class Hero_unit(): def __init__(self, nickname, aggressivity, life_value): self.nickname = nickname self.aggressivity = aggressivity self.life_value = life_value def attack(self, enemy): enemy.life_value -= self.aggressivity def talk(self): print('Your Hero:') @staticmethod def random_hero(): name_list = ['Grom·Hellscream', 'Ogrim·Doomhammer', ] random_name = random.choice(name_list) random_agg = random.randint(45, 65) random_life_value = random.randint(395, 650) return Hero_unit(random_name, random_agg, random_life_value)
#该方法返回一个Hero_unit类的对象 class Blademaster(Hero_unit): pass b1 = Blademaster.random_hero() print(b1) 运行结果: <__main__.Hero_unit object at 0x00000000021F37F0>
# staticmethod返回的依旧是Hero_unit的对象,而不是Blademaster类
这样显然不能通过Blademaster类产生,我们用__str__方法定义一下通过不同类产生对象后的输出:
import random class Hero_unit(): def __init__(self, nickname, aggressivity, life_value): self.nickname = nickname self.aggressivity = aggressivity self.life_value = life_value def attack(self, enemy): enemy.life_value -= self.aggressivity def talk(self): print('Your Hero:') @staticmethod def random_hero(): name_list = ['Grom·Hellscream', 'Ogrim·Doomhammer', ] random_name = random.choice(name_list) random_agg = random.randint(45, 65) random_life_value = random.randint(395, 650) return Hero_unit(random_name, random_agg, random_life_value) def __str__(self): return 'Hero %s from Hero_unit'%self.nickname class Blademaster(Hero_unit): def __str__(self): return 'Hero %s from Blademaster' % self.nickname b1 = Blademaster.random_hero() print(b1) 运行结果: Hero Grom·Hellscream from Hero_unit # 我们想要实现的结果是Hero %s from Blademaster
换做使用classmethod后,实现了这种子类调用父类方法,产生对象来自子类的需求
import random class Hero_unit(): def __init__(self, nickname, aggressivity, life_value): self.nickname = nickname self.aggressivity = aggressivity self.life_value = life_value def attack(self, enemy): enemy.life_value -= self.aggressivity def talk(self): print('Your Hero:') def __str__(self): return 'Hero %s from Hero_unit'%self.nickname @classmethod def random_hero(cls): name_list = ['Grom·Hellscream', 'Ogrim·Doomhammer', ] random_name = random.choice(name_list) random_agg = random.randint(45,65) random_life_value = random.randint(395,650) return cls(random_name,random_agg,random_life_value) class Blademaster(Hero_unit): def __str__(self): return 'Hero %s from Blademaster' % self.nickname b1 = Blademaster.random_hero() print(b1) 运行结果: Hero Grom·Hellscream from Blademaster
八、反射
python面向对象中的反射:通过字符串的形式操作对象相关的属性。python中的一切事物都是对象(都可以使用反射)
四个可以实现反射的函数
下列方法适用于类和对象(一切皆对象,类本身也是一个对象)
class Hero_unit(): def __init__(self, nickname, aggressivity, life_value): self.nickname = nickname self.aggressivity = aggressivity self.life_value = life_value def attack(self, enemy): enemy.life_value -= self.aggressivity def talk(self): print('Your Hero:') h1 = Hero_unit('BladeMaster',56,425) # hasattr print(hasattr(h1,'nickname')) # getattr hero_name = getattr(h1,'nickname') print(hero_name) hero_armor = getattr(h1,'armor','no attribute armor') print(hero_armor) # setattr setattr(h1,'nickname','DemonHunter') print(h1.nickname) # delattr delattr(h1,'nickname') # print(h1.nickname) # 不存在报错
类也是对象,针对类也是可以使用:
print(getattr(Hero_unit,'talk')) 运行结果: <function Hero_unit.talk at 0x000000000271DB70>
class Ftpclient: def __init__(self,host): self.host = host print('Connect %s...'%self.host) def run(self): while True: cmd = input('>>') in_list = cmd.strip().split() if hasattr(self,in_list[0]): fun = getattr(self,in_list[0]) fun(in_list[1]) def get(self,file_name): print('downloading %s'%file_name) conn1 = Ftpclient('172.16.151.57') conn1.run() 运行结果: Connect 172.16.151.57... >>get 1.txt downloading 1.txt >>
m = __import__('sys') print(m.path) import importlib m = importlib.import_module('sys') print(m.path)

浙公网安备 33010602011771号