标签:第一个 ict taf img 失败 添加 ide 调用函数 企业
面向对象编程——Object Oriented Programming,简称OOP,是一种程序设计思想。OOP把对象作为程序的基本单元,一个对象包含了数据和操作数据的函数。
面向过程的程序设计把计算机程序视为一系列的命令集合,即一组函数的顺序执行。为了简化程序设计,面向过程把函数继续切分为子函数,即把大块函数通过切割成小块函数来降低系统的复杂度。
而面向对象的程序设计把计算机程序视为一组对象的集合,而每个对象都可以接收其他对象发过来的消息,并处理这些消息,计算机程序的执行就是一系列消息在各个对象之间传递。
在Python中,所有数据类型都可以视为对象,当然也可以自定义对象。自定义的对象数据类型就是面向对象中的类(Class)的概念。
面向过程的程序设计的核心是(流水线式思维),过程即解决问题的步骤,面向过程的设计就好比精心设计好的一条流水线 ,考虑周全什么时候处理什么东西。
面向对象的程序设计的核心是对象(上帝式思维),要理解对象为何物,必须把自己当成上帝,上帝眼里世间存在的万物皆为对象,不存在的也可以创造出来。面向对象的程序设计好比如来设计西游记,如来要解决的问题是把经书传给东土大唐,如来想了想解决这个问题需要四个人:唐僧,沙和尚,猪八戒,孙悟空,每个人都有各自的特征和技能(这就是对象的概念,特征和技能分别对应对象的属性和方法),然而这并不好玩,于是如来又安排了一群妖魔鬼怪,为了防止师徒四人在取经路上被搞死,又安排了一群神仙保驾护航,这些都是对象。然后取经开始,师徒四人与妖魔鬼怪神仙互相缠斗着直到最后取得真经。如来根本不会管师徒四人按照什么流程去取。
在python 中面向对象的程序设计并不是全部。
面向对象编程可以使程序的维护和扩展变得更简单,并且可以大大提高程序开发效率 ,另外,基于面向对象的程序可以使它人更加容易理解你的代码逻辑,从而使团队开发变得更从容。
了解一些名词:类、对象、实例、实例化
示例说明面向过程和面向对象在程序流程上的不同之处。
假设我们要处理学生的成绩表,为了表示一个学生的成绩,面向过程的程序可以用一个dict表示:
std1 = { ‘name‘: ‘Michael‘, ‘score‘: 98 } std2 = { ‘name‘: ‘Bob‘, ‘score‘: 81 }
而处理学生成绩可以通过函数实现,比如打印学生的成绩:
def print_score(std): print(‘%s: %s‘ % (std[‘name‘], std[‘score‘]))
如果采用面向对象的程序设计思想,我们首选思考的不是程序的执行流程,而是Student
这种数据类型应该被视为一个对象,这个对象拥有name
和score
这两个属性(Property)。如果要打印一个学生的成绩,首先必须创建出这个学生对应的对象,然后,给对象发一个print_score
消息,让对象自己把自己的数据打印出来。
class Student(object): def __init__(self, name, score): self.name = name self.score = score def print_score(self): print(‘%s: %s‘ % (self.name, self.score))
给对象发消息实际上就是调用对象对应的关联函数,我们称之为对象的方法(Method)。面向对象的程序写出来就像这样:
bart = Student(‘Bart Simpson‘, 59) lisa = Student(‘Lisa Simpson‘, 87) bart.print_score() lisa.print_score()
def functionName(args): ‘函数文档字符串‘ 函数体 ‘‘‘ class 类名: ‘类的文档字符串‘ 类体 ‘‘‘ #创建一个类 class Data: pass
注意:类名通常是大写开头的单词
class Person: #定义一个人类 role = ‘person‘ #人的角色属性都是人 def walk(self): #人都可以走路,也就是有一个走路方法 print("person is walking...") print(Person.role) #查看人的role属性 print(Person.walk) #引用人的走路方法,注意,这里不是在调用
实例化:类名加括号就是实例化,会自动触发__init__函数的运行,可以用它来为每个实例定制自己的特征
class Person: #定义一个人类 role = ‘person‘ #人的角色属性都是人 def __init__(self,name): self.name = name # 每一个角色都有自己的昵称; def walk(self): #人都可以走路,也就是有一个走路方法 print("person is walking...") print(Person.role) #查看人的role属性 print(Person.walk) #引用人的走路方法,注意,这里不是在调用
实例化的过程就是类——>对象的过程
语法:对象名 = 类名(参数)
p1 = Person()
self:在实例化时自动将对象/实例本身传给__init__的第一个参数,你也可以给他起个别的名字,但是长的帅的人都不会这么做。
因为你瞎改别人就不认识
类属性的补充
一:我们定义的类的属性到底存到哪里了?有两种方式查看 dir(类名):查出的是一个名字列表 类名.__dict__:查出的是一个字典,key为属性名,value为属性值 二:特殊的类属性 类名.__name__# 类的名字(字符串) 类名.__doc__# 类的文档字符串 类名.__base__# 类的第一个父类(在讲继承时会讲) 类名.__bases__# 类所有父类构成的元组(在讲继承时会讲) 类名.__dict__# 类的字典属性 类名.__module__# 类定义所在的模块 类名.__class__# 实例对应的类(仅新式类中)
格式:对象名 = 类名(参数列表)
注意:没有参数,小括号也不能省略
访问对象的属性和方法
格式:对象名.属性名
赋值:对象名.属性名 = 新值
格式:对象名.方法名(参数列表)
class 类名: def __init__(self,参数1,参数2): self.对象的属性1 = 参数1 self.对象的属性2 = 参数2 def 方法名(self):pass def 方法名2(self):pass 对象名 = 类名(1,2) #对象就是实例,代表一个具体的东西 #类名() : 类名+括号就是实例化一个类,相当于调用了__init__方法 #括号里传参数,参数不需要传self,其他与init中的形参一一对应 #结果返回一个对象 对象名.对象的属性1 #查看对象的属性,直接用 对象名.属性名 即可 对象名.方法名() #调用类中的方法,直接用 对象名.方法名() 即可
# 在终端输出如下信息 ‘‘‘ 小明,10岁,男,上山去砍柴 小明,10岁,男,开车去东北 小明,10岁,男,最爱大保健 老李,90岁,男,上山去砍柴 老李,90岁,男,开车去东北 老李,90岁,男,最爱大保健 ‘‘‘ class Person(object): def __init__(self, name, age, sex): self.name = name self.age = age self.sex = sex def print_hooby(self, hobby): print ("%s, %s岁, %s, %s "%(self.name, self.age, self.sex, hobby)) p1 = Person("小明", 10, "男") p1.print_hooby("上山去砍柴") p1.print_hooby("开车去东北") p1.print_hooby("最爱大保健") p2 = Person("老李", 90, "男") p2.print_hooby("上山去砍柴") p2.print_hooby("开车去东北") p2.print_hooby("最爱大保健")
# 计算一个类实例过多少个对象 class Count: count = 0 def __init__(self): Count.count = self.count + 1 a = Count() b = Count() c = Count() d = Count() print(Count.count)
from math import pi class Circle: """ 定义了一个圆形类; 提供计算面积(area)和计算周长(perimeter)的方法 """ def __init__(self, radius): self.radius = radius def area(self): return self.radius ** 2 * pi def perimeter(self): return self.radius * 2 * pi circle = Circle(3) # 实例化一个圆 area1 = circle.area() # 计算圆面积 per1 = circle.perimeter() # 计算圆周长 print(area1, per1) # 打印圆面积和周长
创建一个类就会创建一个类的名称空间,用来存储类中定义的所有名字,这些名字称为类的属性
类有两种属性:静态属性和动态属性
其中类的数据属性是共享给所有对象的
class Person: name = "小白" def fun1(self): print(self.name) p1 = Person() p2 = Person() print(id(p1.name)) 2436828402064 print(id(Person.name)) 2436828402064
而类的动态属性是绑定到所有对象的
class Person: name = "小白" def fun1(self): print(self.name) p1 = Person() p2 = Person() print(p1.fun1) <bound method Person.fun1 of <__main__.Person object at 0x000002375E4A8C18>> print(Person.fun1) <function Person.fun1 at 0x000002375E4AB8C8>
创建一个对象/实例就会创建一个对象/实例的名称空间,存放对象/实例的名字,称为对象/实例的属性
查询顺序:
对象与对象之间是互相独立的.
组合指的是,给一个类的对象封装一个属性,这个属性是另一个类的对象
圆环是由两个圆组成的,圆环的面积是外面圆的面积减去内部圆的面积。圆环的周长是内部圆的周长加上外部圆的周长。
这个时候,我们就首先实现一个圆形类,计算一个圆的周长和面积。然后在"环形类"中组合圆形的实例作为自己的属性来用
from math import pi class Circle: """ 定义了一个圆形类; 提供计算面积(area)和计算周长(perimeter)的方法 """ def __init__(self, radius): self.radius = radius def area(self): return self.radius ** 2 * pi def perimeter(self): return self.radius * 2 * pi circle = Circle(3) # 实例化一个圆 area1 = circle.area() # 计算圆面积 per1 = circle.perimeter() # 计算圆周长 print(area1, per1) # 打印圆面积和周长 class Ring: def __init__(self, radius_outside, radius_inside): """ :param radius_outside: 外圆的半径 :param radius_inside: 内圆的半径 """ self.outside_redius = Circle(radius_outside) self.inside_redius = Circle(radius_inside) def rin_area(self): " 计算圆环的面积,用外圆的面积 - 内圆的面积" return self.outside_redius.area() - self.inside_redius.area() def rin_perimeter(self): " 计算圆环的周长,用外圆的周长 + 内圆的周长" return self.outside_redius.perimeter() + self.inside_redius.perimeter() ring = Ring(9, 3) # 实例化一个圆环 rinArea = ring.rin_area() # 计算圆环的面积 rinPer = ring.rin_perimeter() # 计算圆环的周长 print(rinArea, rinPer) # 打印圆环的面积和周长
""" 模拟英雄联盟写一个游戏人物的类. 要求: (1)创建一个 Game_role的类. (2) 构造方法中给对象封装name,ad(攻击力),hp(血量).三个属性. (3) 创建一个attack方法,此方法是实例化两个对象,互相攻击的功能: 例: 实例化一个对象 盖伦,ad为10, hp为100 实例化另个一个对象 剑豪 ad为20, hp为80 盖伦通过attack方法攻击剑豪,此方法要完成 ‘谁攻击谁,谁掉了多少血, 还剩多少血‘的提示功能. """ class Gamerole: def __init__(self, name, ad, hp): self.name = name self.ad = ad self.hp = hp def attack(self, p1): hp = p1.hp - self.ad print("%s攻击了%s, %s掉了%s血, 还剩%s血" % (self.name, p1.name, p1.name, self.ad, hp)) gailun = Gamerole("盖伦", 10, 100) jianhao = Gamerole("剑豪", 20, 80) gailun.attack(jianhao) # 添加武器进行攻击:斧子,刀,枪,棍,棒..., # 版本一: # 代码不合理: 人物利用武器攻击别人,你的动作发起者是人,而不是武器. class Gamerole: def __init__(self, name, ad, hp): self.name = name self.ad = ad self.hp = hp def attack(self, p1): hp = p1.hp - self.ad print("%s攻击了%s, %s掉了%s血, 还剩%s血" % (self.name, p1.name, p1.name, self.ad, hp)) class Arms: def __init__(self, name, ad): self.name = name self.ad = ad def fight(self, p1, p2): hp = p2.hp - self.ad print("%s使用%s攻击了%s, %s掉了%s血, 还剩下%s血" % (p1.name, self.name, p2.name, p2.name, self.ad, hp)) gailun = Gamerole("盖伦", 10, 100) jianhao = Gamerole("剑豪", 20, 80) dabaodao = Arms("大宝刀", 20) dabaodao.fight(gailun, jianhao) # 版本二:通过组合来实现,让代码合理 class Gamerole: def __init__(self, name, ad, hp): self.name = name self.ad = ad self.hp = hp def armament_weapon(self, aex): # 调用时传入武器对象 self.aex = aex class Arms: def __init__(self, name, ad): self.name = name self.ad = ad def fight(self, p1, p2): hp = p2.hp - self.ad print("%s使用%s攻击了%s, %s掉了%s血, 还剩下%s血" % (p1.name, self.name, p2.name, p2.name, self.ad, hp)) gailun = Gamerole("盖伦", 10, 100) jianhao = Gamerole("剑豪", 20, 80) dabaodao = Arms("大宝刀", 20) gailun.armament_weapon(dabaodao) # 给盖伦装备了大宝刀这个武器 gailun.aex.fight(gailun, jianhao)
面向对象有三大特性:继承、封装、和多态。
继承: 单继承,多继承.
继承是一种创建新类的方式,在Python中,新建的类可以继承一个或者多个父类,父类又称为基类或超类,新建的类称为派生类或子类
子类可以自动拥有父类中除了私有属性外的其他所有内容。说?了, ??可以随便用爹的东西。但是朋友们, 一定要认清楚?个事情。必须先有爹, 后有??。 顺序不能乱,在Python中实现继承非常简单。在声明类的时候, 在类名后?面添加一个小括号,把要继承的类传进去就可以完成继承关系。
那么什么情况可以使用继承呢?
单纯的从代码层?上来看,当两个类具有相同的功能或者特征的时候,可以采用继承的形式。提取一个父类,这个父类中编写两个类中相同的部分。然后两个类分别去继承这个类就可以了。这样写的好处是我们可以避免写很多重复的功能和代码。
class Animal: def eat(self): print "%s 吃 " %self.name def drink(self): print "%s 喝 " %self.name def shit(self): print "%s 拉 " %self.name def pee(self): print "%s 撒 " %self.name class Cat(Animal): def __init__(self, name): self.name = name self.breed = ‘猫‘ def cry(self): print ‘喵喵叫‘ class Dog(Animal): def __init__(self, name): self.name = name self.breed = ‘狗‘ def cry(self): print ‘汪汪叫‘ # ######### 执行 ######### c1 = Cat(‘小白家的小黑猫‘) c1.eat() c2 = Cat(‘小黑的小白猫‘) c2.drink() d1 = Dog(‘胖子家的小瘦狗‘) d1.eat()
综合示例:
动物都有吃、喝、拉、撒共同属性,各动物有各动物的方法. 定义一个动物类(Animal),定义一个鸟类(Bird),定义一个马类(Horse)
class Animal(object): def __init__(self, name, sex, age): self.name = name self.sex = sex self.age = age def eat(self): print("%s 在吃东西" % self.name) def drink(self): print("%s 在喝东西" % self.name) def pull(self): print("%s 在拉粑粑" % self.name) def sow(self): print("%s 在嘘嘘" % self.name) class Bird(Animal): def flight(self): print("%s 在自由的飞翔" % self.name) class Horse(Animal): def running(self): print("%s 在飞驰的奔跑" % self.name) bird1 = Bird("麻雀", 1.2, "公") # 实例化一只鸟 bird1.flight() # 调用自身独有的方法 bird1.eat() # 调用父类公共的方法 horse1 = Horse("草原马", 3, "公") # 实例化一匹马 horse1.running() # 调用自身独有的方法 horse1.pull() # 调用父类公共的方法
现在要给鸟类添加一个翅膀的属性,给马类添加一个尾巴的属性
class Animal(object): def __init__(self, name, sex, age): self.name = name self.sex = sex self.age = age def eat(self): print("%s 在吃东西" % self.name) def drink(self): print("%s 在喝东西" % self.name) def pull(self): print("%s 在拉粑粑" % self.name) def sow(self): print("%s 在嘘嘘" % self.name) class Bird(Animal): def __init__(self, name, sex, age, wing): # 方法一:通过父类名.父类方法名(参数) # Animal.__init__(self, name, sex, age) # 方法二:通过super().父类方法名(参数(自动传入self)) super().__init__(name, sex, age) # 还可以这样写super(Bird,self).__init(name, sex, age) self.wing = wing def flight(self): print("%s 在自由的飞翔" % self.name) class Horse(Animal): def __init__(self, name, sex, age, tail): # Animal.__init__(self, name, sex, age) super().__init__(name, sex, age) self.tail = tail def running(self): print("%s 在飞驰的奔跑" % self.name) bird2 = Bird("鹦鹉", 6, "母", "红翅膀") # 实例化一只鸟,加入翅膀 print(bird2.__dict__) # 查看是否将翅膀属性成功添加 # {‘name‘: ‘鹦鹉‘, ‘sex‘: 6, ‘age‘: ‘母‘, ‘wing‘: ‘红翅膀‘} horse2 = Horse("汗血马", 3, "公", "长尾巴") # 实例化一匹马,加入尾巴 print(horse2.__dict__) # 查看是否将尾巴属性成功添加 # {‘name‘: ‘汗血马‘, ‘sex‘: 3, ‘age‘: ‘公‘, ‘tail‘: ‘长尾巴‘}
总结:
只执行父类的方法:子类中不要定义与父类同名的方法
只执行子类的方法:在子类创建这个方法
问题:既要执行子类的方法,又要执行父类的方法?
有两种解决方法:
1、在子类中执行父类的方法
2、通过super()
类: 经典类, 新式类
class A(object): pass
class A: pass
单继承: 新式类,经典类查询顺序一样
多继承:
经典类多继承:
class A: def func(self): print(" IN A") class B(A): def func(self): print(" IN B") class C(A): def func(self): print(" IN C") class D(B): def func(self): print(" IN D") class E(C): def func(self): print(" IN E") class F(D, E): def func(self): print(" IN F") func1 = F() func1.func() # 执行func方法时: # 首先去F类找,如果F没有去D类找,如果D类没有去B类找,如果B类没有去A类找,如果A类没有去E类找,如果E类没有去C类找,如果C类还没有则报错 # 查找顺序为F——>D——>B——>A——>E——>C # 在上述查找func方法的过程中,一旦找到,则寻找过程立即中断,便不会再继续找了
新式类多继承:
class A(object): def func(self): print(" IN A") class B(A): def func(self): print(" IN B") class C(A): def func(self): print(" IN C") class D(B): def func(self): print(" IN D") class E(C): def func(self): print(" IN E") class F(D, E): def func(self): print(" IN F") func1 = F() func1.func() print(F.mro()) # 查询类的继承顺序 # 打印结果:[<class ‘__main__.F‘>, <class ‘__main__.D‘>, <class ‘__main__.B‘>, <class ‘__main__.E‘>, <class ‘__main__.C‘>, <class ‘__main__.A‘>, <class ‘object‘>] # 执行func方法时: # 首先去F类找,如果F没有去D类找,如果D类没有去B类找,如果B类没有去E类找,如果E类没有去C类找,如果C类没有去A类找,如果A类还没有则报错 # 查找顺序为F——>D——>B——>E——>C——>A # 在上述查找func方法的过程中,一旦找到,则寻找过程立即中断,便不会再继续找了
面向对象为什么要有继承?继承的好处是什么?
接口类
为什么要使用接口类?
接口提取了一群类共同的函数,可以把接口当做一个函数的集合。
然后让子类去实现接口中的函数。
这么做的意义在于归一化,什么叫归一化,就是只要是基于同一个接口实现的类,那么所有的这些类产生的对象在使用时,从用法上来说都一样。
归一化的好处在于:
比如:我们定义一个动物接口,接口里定义了有跑、吃、呼吸等接口函数,这样老鼠的类去实现了该接口,松鼠的类也去实现了该接口,由二者分别产生一只老鼠和一只松鼠送到你面前,即便是你分别不到底哪只是什么鼠你肯定知道他俩都会跑,都会吃,都能呼吸。
再比如:我们有一个汽车接口,里面定义了汽车所有的功能,然后由本田汽车的类,奥迪汽车的类,大众汽车的类,他们都实现了汽车接口,这样就好办了,大家只需要学会了怎么开汽车,那么无论是本田,还是奥迪,还是大众我们都会开了,开的时候根本无需关心我开的是哪一类车,操作手法(函数调用)都一样
接口类示例
需求说明:开发一个支付功能、支持微信支付、支付宝支付、Apple支付等...
版本一:采用归一化设计
# 版本一:采用归一化设计 class Alypay(object): """ 支付宝支付 """ def pay(self, money): print("支付宝支付了 %s 元" % money) class Wechatpay(object): """ 微信支付 """ def pay(self, money): print("微信支付了 %s 元" % money) class Applepay(object): """ apple支付 """ def pay(self, money): print("apple支付了 %s 元" % money) def apy(obj, money): obj.pay(money) a1 = Alypay() w1 = Wechatpay() p1 = Applepay() apy(a1, 200) apy(w1, 100) apy(p1, 50)
按照上面这种方法是可行的, 但是有个问题, 如果第二个程序员去更改你的代码,如果类中不是定义的pay函数去支付, 那么就会报错,列如下面将apple支付里面的函数(pay)变为(applepay).
版本二:
class Alypay(object): """ 支付宝支付 """ def pay(self, money): print("支付宝支付了 %s 元" % money) class Wechatpay(object): """ 微信支付 """ def pay(self, money): print("微信支付了 %s 元" % money) class Applepay(object): """ apple支付 """ def applepay(self, money): print("apple支付了 %s 元" % money) def apy(obj, money): obj.pay(money) a2 = Alypay() w2 = Wechatpay() p2 = Applepay() apy(a2, 200) apy(w2, 100) apy(p2, 50) # 这里执行就会报错. 打破了归一化设计原则
版本三:通过abc模块来实现接口,如果类中定义的非模块指定的函数名,我们让其报错。
from abc import ABCMeta, abstractclassmethod class Payment(metaclass=ABCMeta): @abstractclassmethod def pay(self, money): pass class Alypay(Payment): """ 支付宝支付 """ def pay(self, money): print("支付宝支付了 %s 元" % money) class Wechatpay(Payment): """ 微信支付 """ def pay(self, money): print("微信支付了 %s 元" % money) class Applepay(Payment): """ apple支付 """ def Applepay(self, money): print("apple支付了 %s 元" % money) def apy(obj, money): obj.pay(money) a1 = Alypay() w1 = Wechatpay() p1 = Applepay() # 执行报错: TypeError: Can‘t instantiate abstract class Applepay with abstract methods pay apy(a1, 200) apy(w1, 100) apy(p1, 50)
最终版本:
from abc import ABCMeta, abstractclassmethod class Payment(metaclass=ABCMeta): @abstractclassmethod def pay(self, money): pass class Alypay(Payment): """ 支付宝支付 """ def pay(self, money): print("支付宝支付了 %s 元" % money) class Wechatpay(Payment): """ 微信支付 """ def pay(self, money): print("微信支付了 %s 元" % money) class Applepay(Payment): """ apple支付 """ def pay(self, money): print("apple支付了 %s 元" % money) def apy(obj, money): obj.pay(money) a1 = Alypay() w1 = Wechatpay() p1 = Applepay() apy(a1, 200) apy(w1, 100) apy(p1, 50)
抽象类
抽象类是一个特殊的类,它的特殊之处在于只能被继承,不能被实例化; Python抽象类是借助模块来实现
如果说类是从一堆对象中抽取相同的内容而来的,那么抽象类就是从一堆类中抽取相同的内容而来的,内容包括数据属性和函数属性。
比如我们又香蕉的类,有苹果的类,有桃子的类,从这些类抽取相同的内容就是水果这个抽象的类,你吃水果时,要么是吃一个具体的香蕉,要么是吃一个具体的桃子。。。。你永远无法吃到一个叫做水果的东西。
从设计角度看,如果类是从现实对象抽取而来的,那么抽象类就是基于类抽象而来的。
从实现角度看,抽象类与普通类的不同之处在于:抽象类中有抽象的方法,该类不能被实例化,只能被继承,且子类必须实现抽象方法。这一点与接口类有点类似,但其实是不同的。
在Python中实现抽象类
# 一切皆文件 import abc # 利用abc模块实现抽象类 class All_file(metaclass=abc.ABCMeta): all_type = ‘file‘ @abc.abstractclassmethod # 定义抽象方法,无需实现功能 def read(self): ‘子类必须定义读功能‘ pass @abc.abstractclassmethod # 定义抽象方法,无需实现功能 def write(self): ‘子类必须定义写功能‘ pass # class Txt(All_file): # pass # # t1 = Txt() # 报错,子类没有定义抽象方法 # 报错内容 TypeError: Can‘t instantiate abstract class Txt with abstract methods read, write 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("进程数据的写方法") wenbenfile = Txt() satafile = Sata() processfile = Process() # 这样大家都被归一化了,也就是一切皆文件的思想 wenbenfile.read() satafile.write() processfile.read() print(wenbenfile.all_type) print(satafile.all_type) print(processfile.all_type)
抽象类的本质还是类,指的是一组类的相似性,包括数据属性(如all_type)和函数属性(如read、write),而接口只强调函数属性的相似性。
抽象类是一个介于类和接口直接的一个概念,同时具备类和接口的部分特性,可以用来实现归一化设计
在python中,并没有接口类这种东西,即便不通过专门的模块来定义接口,我们也应该有一些基本的概念。
1、多继承问题
在继承抽象类的过程中,应该尽量避免多继承;
而在继承接口的时候,反而鼓励多继承接口
接口隔离原则:
使用多个专门的接口,而不使用单一的总接口。即客户端不应该依赖那些不需要的接口。
2、方法的实现
在抽象类中,我们可以对一些抽象方法做出基础实现;
而在接口类中,任何方法都只是一种规范,具体的功能需要子类实现
多态指的是一类事物的多种形态
动物有多种形态:人、狗、猪
import abc class Animal(metaclass=abc.ABCMeta): #同一类事物:动物 @abc.abstractmethod def talk(self): pass class People(Animal): #动物的形态之一:人 def talk(self): print(‘say hello‘) class Dog(Animal): #动物的形态之二:狗 def talk(self): print(‘say wangwang‘) class Pig(Animal): #动物的形态之三:猪 def talk(self): print(‘say aoao‘)
文件有多种形态:文本文件、可执行文件
import abc class File(metaclass=abc.ABCMeta): #同一类事物:文件 @abc.abstractmethod def click(self): pass class Text(File): #文件的形态之一:文本文件 def click(self): print(‘open file‘) class ExeFile(File): #文件的形态之二:可执行文件 def click(self): print(‘execute file‘)
多态性是指在不考虑实例类型的情况下使用实例
在面向对象方法中一般是这样表述多态性:
向不同的对象发送同一条消息(!!!obj.func():是调用了obj的方法func,又称为向obj发送了一条消息func),不同的对象在接收时会产生不同的行为(即方法)。
也就是说,每个对象可以用自己的方式去响应共同的消息。所谓消息,就是调用函数,不同的行为就是指不同的实现,即执行不同的函数。
比如:老师.下课铃响了(),学生.下课铃响了(),老师执行的是下班操作,学生执行的是放学操作,虽然二者消息一样,但是执行的效果不同
多态性
peo=People() dog=Dog() pig=Pig() #peo、dog、pig都是动物,只要是动物肯定有talk方法 #于是我们可以不用考虑它们三者的具体是什么类型,而直接使用 peo.talk() dog.talk() pig.talk() #更进一步,我们可以定义一个统一的接口来使用 def func(obj): obj.talk()
鸭子类型
逗比时刻:
Python崇尚鸭子类型,即‘如果看起来像、叫声像而且走起路来像鸭子,那么它就是鸭子’
python程序员通常根据这种行为来编写程序。例如,如果想编写现有对象的自定义版本,可以继承该对象也可以创建一个外观和行为像,但与它无任何关系的全新对象,后者通常用于保存程序组件的松耦合度。
例1:利用标准库中定义的各种‘与文件类似’的对象,尽管这些对象的工作方式像文件,但他们没有继承内置文件对象的方法
class TxtFile(object): def read(self): pass def write(self): pass class DiskFile(object): def read(self): pass def write(self): pass
例2:序列类型有多种形态:字符串,列表,元组,但他们直接没有直接的继承关系
class TxtFile(object): def read(self): pass def write(self): pass class DiskFile(object): def read(self): pass def write(self): pass # str, list, tuple 都是序列类型 s = str(‘hello‘) l = list([1, 2, 3]) t = tuple((4, 5, 6)) # 我们可以在不考虑三者类型的前提下使用s, l, t s.__len__() l.__len__() t.__len__() len(s) len(l) len(t)
封装
好处
封装原则
在Python中使用双下划线开头的方式将属性隐藏起来(设置为私有的)
私有变量
# 其实这仅仅只是一种变形操作 # 类中所有双下划线开头的名称如__x 都会自动形成:_类名__x的新式: class A(object): name = "小白" __age = 18 # 类的数据属性就应该是共享的,但是语法上是可以把类的数据属性设置成私有的如__age,会变形为_A__age def __init__(self): self.__x = 10 # 变形为self._A__X def __foo(self): # 变形为_A__foo print(self.__age) print("From A") def fun1(self): self.__foo() # 只有在类内部才可以通过__foo的形式访问到. obj1 = A() print(obj1.name) # print(obj1.__age) # 执行报错;因为实例化对象不能访问私有变量 # print(A.__age) # 执行报错;因为类名不能访问私有变量 # 对于私有变量,类的外部不能访问. obj1.fun1() # 对于私有静态变量,类的内部可以访问. print(A._A__age) # A._A__age是可以访问到的,即这种操作并不是严格意义上的限制外部访问,仅仅只是一种语法意义上的变形
对于私有变量来说,只能在本类中内部访问,类的外部,派生类均不可访问.
这种自动变形的特点:
1.类中定义的__x只能在内部使用,如self.__x,引用的就是变形的结果
2.这种变形其实正是针对外部的变形,在外部是无法通过__x这个名字访问到的。
3.在子类定义的__x不会覆盖在父类定义的__x,因为子类中变形成了:_子类名__x,而父类中变形成了:_父类名__x,即双下滑线开头的属性在继承给子类时,子类是无法覆盖的。
这种变形需要注意的问题是:
1.这种机制也并没有真正意义上限制我们从外部直接访问属性,知道了类名和属性名就可以拼出名字:_类名__属性,然后就可以访问了,如a._A__N
2.变形的过程只在类的内部生效,在定义后的赋值操作,不会变形
私有方法
在继承中,父类如果不想让子类覆盖自己的方法,可以将方法定义为私有的
class A(object): def func1(self): print(" In A ") def func2(self): self.func1() class B(A): def func1(self): print(" In B ") c1 = B() c1.func2() # 打印结果: In B class A(object): def __func1(self): print(" In A ") def func2(self): self.__func1() class B(A): def __func1(self): print(" In B ") c2 = B() c2.func2() # 打印结果: In A
封装在于明确区分内外,使得类实现者可以修改封装内的东西而不影响外部调用者的代码;而外部使用用者只知道一个接口(函数),只要接口(函数)名、参数不变,使用者的代码永远无需改变。这就提供一个良好的合作基础——或者说,只要接口这个基础约定不变,则代码改变不足为虑。
# 类的设计者 class Room(object): def __init__(self, name, owner, length, width, high): self.name = name self.owner = owner self.__length = length self.__width = width self.__high = high def tell_area(self): return self.__length * self.__width # 对外提供的接口,隐藏了内部的实现细节,此时我们想求的是面积 # 使用者 r1 = Room("客厅", "小白", 20, 20 ,20) area = r1.tell_area() # 使用者调用接口tell_area print(area) # 类的设计者,轻松的扩展了功能,而类的使用者完全不需要改变自己的代码 class Room(object): def __init__(self, name, owner, length, width, high): self.name = name self.owner = owner self.__length = length self.__width = width self.__high = high def tell_area(self): # 对外提供的接口,隐藏内部实现,此时我们想求的是体积,内部逻辑变了,只需求修该下列一行就可以很简答的实现,而且外部调用感知不到,仍然使用该方法,但是功能已经变了 return self.__length * self.__width * self.__high # 使用者 r1 = Room("客厅", "小白", 20, 20 ,20) area = r1.tell_area() # 对于仍然在使用tell_area接口的人来说,根本无需改动自己的代码,就可以用上新功能 print(area)
属性:将一个方法伪装成一个属性;在代码的级别上没有本质的提升,但是让其看起来跟合理.
示例一:
例一: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 Bmi(object): def __init__(self, name, weight, height): self.name = name self.weight = weight self.height = height @property def calculation_bmi(self): return "%s 的bmi 值%s" % (self.name, self.weight / self.height ** 2) p1 = Bmi("小白", 54, 1.65) # print(p1.calculation_bmi()) print(p1.calculation_bmi)
示例二:
import math class Circle(object): def __init__(self, radius): """ :param 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.area) # 可以向访问数据属性一样去访问area,会触发一个函数的执行,动态计算出一个值 print(c.perimeter) # 同上 ‘‘‘ 输出结果: 314.1592653589793 62.83185307179586 ‘‘‘ #注意:此时的特性area和perimeter不能被赋值 c.area=3 #为特性area赋值 ‘‘‘ 抛出异常: AttributeError: can‘t set attribute ‘‘‘
为什么要使用property
将一个类的函数定义成特性以后,对象再去使用的时候obj.name,根本无法察觉自己的name是执行了一个函数然后计算出来的,这种特性的使用方式遵循了统一访问的原则
列如
ps:面向对象的封装有三种方式:
【public】
这种其实就是不封装,是对外公开的
【protected】
这种封装方式对外不公开,但对朋友(friend)或者子类(形象的说法是“儿子”,但我不知道为什么大家 不说“女儿”,就像“parent”本来是“父母”的意思,但中文都是叫“父类”)公开
【private】
这种封装对谁都不公开
属性的改值:
class Person(object): def __init__(self, name, age): self.name = name self.__age = age if type(age) is int else print("您输入的年龄类型有误,请输入int类型") @property # 将一个方法伪装成一个属性 def age(self): return self.__age # obj.age访问的是self.__age(这也是真实值的存放位置) @age.setter def age(self, a1): """判断,修改的年龄必须是数字""" self.__age = a1 if type(a1) is int else print("您输入的年龄类型有误,请输入int类型") @age.deleter def age(self): del self.__age p1 = Person("小白", 20) print(p1.age) p1.age = 22 # 属性的改值, 一运行,就会自动执行类中的@age.setter下的函数 print(p1.age) del p1.age
一个静态属性property本质就是实现了get, set, delete三种方法
class Foo: @property def AAA(self): print(‘get的时候运行我啊‘) @AAA.setter def AAA(self,value): print(‘set的时候运行我啊‘) @AAA.deleter def AAA(self): print(‘delete的时候运行我啊‘) #只有在属性AAA定义property后才能定义AAA.setter,AAA.deleter f1=Foo() f1.AAA f1.AAA=‘aaa‘ del f1.AAA
class Foo: def get_AAA(self): print(‘get的时候运行我啊‘) def set_AAA(self,value): print(‘set的时候运行我啊‘) def delete_AAA(self): print(‘delete的时候运行我啊‘) AAA=property(get_AAA,set_AAA,delete_AAA) #内置property三个参数与get,set,delete一一对应 f1=Foo() f1.AAA f1.AAA=‘aaa‘ del f1.AAA
怎么用?
class Goods(object): def __init__(self): # 原价 self.original_price = 100 # 折扣 self.discount = 0.8 @property def price(self): # 实际价格 = 原价 * 折扣 new_price = self.original_price * self.discount return new_price @price.setter def price(self, value): # 修改商品原价 self.original_price = value @price.deleter def price(self): # 删除商品原价 del self.original_price clothes = Goods() clothes.price # 获取商品价格 print(clothes.price) clothes.price = 200 # 修改商品价格 print(clothes.price) del clothes.price # 删除商品价格
class A(object): def func(self): # 普通方法 print(self) @classmethod def func1(cls): # 类方法 print(cls) a1 = A() a1.func() # 类方法: 通过类名调用的方法,类方法中第一个参数约定俗称cls,python自动将类名(类空间)传给cls. A.func(a1) A.func1() a1.func1() # 对象调用类方法,cls 得到的是类本身.
类方法的应用场景:
class A: @staticmethod def login(username, password): if username == ‘小白‘ and password == 123: print(‘登录成功‘) else: print(‘登录失败...‘) A.login(‘小白‘,1234)
标签:第一个 ict taf img 失败 添加 ide 调用函数 企业
原文地址:https://www.cnblogs.com/yanjieli/p/9989599.html