面向对象(OOP)
#什么是对象
面向对象是一种编程思想,是指导程序员如何编写出更好的程序的思想
核心是对象,程序就是一系列对象的集合,我们程序员只需要调度、控制这些对象来交互完成工作
#案例1,把对象装进冰箱
面向过程:
1.打开冰箱
2.装入冰箱
3.关闭冰箱
面向对象:
找个具备装大象的技能的对象
在面向对象中,程序员的角度发生改变,从具体的操作者变成了指挥者
强调:对象不是凭空产生的,需要我们自己设计
#案例2,西天取经
面向过程:
如来找5个人,经历九九八十一难来取经,在把经书送回去
面向对象:
如来只需要找5个对象,自己负责调度即可,如果某个对象发生变化,也不会影响其他对象的扩展性
#案例3,
面向过程:
曹操吟诗
喝酒吃肉,人生真爽 后改为
喝酒吃肉,人生几何 后改为
对酒当歌,人生几何
面向对象:
把字拆成一个个对象,用的时候组装
优点:
1.扩展性强
2.灵活性强
3.重用性强
缺点:
1.程序的复杂度提高了
2.无法准确预知结果
#使用场景
1.对扩展性要求较高的程序,通常是直接面向用户的(QQ、微信)
#不是所有的程序都要面向对象,要分析具体的需求
面向过程
#面向过程
面向过程关注的是过程,就是先干什么、后干什么
#优点
逻辑清晰,可以将复杂的问题简单化、流程化
#缺点
扩展性差,可维护性差,重用性低
#使用场景
1.对扩展性要求较低的程序,比如系统内核、git、计算器
类和对象
#类和对象
类和对象是面向对象编程的最核心的两个概念
类
#类
类就是类型、类别,类就是一个抽象的概念
类是一系列具备相同特征和相同行为的对象的集合
对象
#对象
对象就是具体存在的某个事物,有自己的特征和行为
#类和对象的关系
类包含一系列对象
一个对象属于某个类
#先有类,还是先有对象
生活中,是先有对象,然后有类
程序中,先有类,才能有对象,我们必须先告诉计算机这类对象有什么特征、有什么行为(也就是说要先创建类)
所以说,在使用面向对象编程时,第一步就是考虑需要使用什么对象,再考虑使用什么类,然后,先创建类,再创建对象
创建类和对象
#创建类
#语法
class 类的名字:
#注释
#类中的内容,描述属性和技能
#描述属性用变量
#描述行为用函数
#类的名字
1.见名知意
2.大驼峰(单词的首字母大写)
#创建类
#一个类,使用类名+()可以创建多个不同的对象
class Student:
pass
#创建对象(类名加括号)
print(Student) #<class类 '__main__.Student'>
res = Student()
print(res) #<__main__.Student object对象 at 0x000001CBB06AFA90>
print(res.__class__.__name__) #Student
print(res.__dict__) #{}
#类中的属性可以被任何一个对象访问,所以类中存放的应该是对象的公共属性
class Person:
name = '张三丰'
age = 2000
sex = 'male'
res = Person()
print(res)
print(res.name)
print(res.age)
print(res.sex)
<__main__.Person object at 0x00000138EAA9A940>
张三丰
2000
male
#可以为对象单独设置属性
#属性的查找顺序为,先查找对象,再查找类
class Person:
eat = '会吃饭'
run = '会跑路'
zhangsan = Person()
zhangsan.eat = '不会吃饭'
lisi = Person()
lisi.run = '不会跑路'
print(zhangsan.eat)
print(zhangsan.run)
print(lisi.eat)
print(lisi.run)
不会吃饭
会跑路
会吃饭
不会跑路
#对象的精髓就在于将数据和处理数据的函数整合到一起,这样以来,当拿到一个对象,就同时拿到处理的数据和处理数据的函数
#在类和对象中,属性(数据)用变量表示,方法用函数表示
#属性、方法也可以统一称为属性,分为数据属性、函数属性
class Student:
school = '清华'
def __init__(self,name,age):
self.name = name
self.age = age
def study(self):
print('hello i am a student, my name is:%s'%self.school)
t1 = Student('syy',18)
print(type(t1.study))
print(type(Student.study))
<class 'method'> #方法
<class 'function'> #函数
属性的增删改查
#类
#增加类的属性
新建一个类(覆盖)
#对象
#增加对象的属性
对象变量名.属性名称 = 属性值
#删除对象的属性
del 对象变量名.属性名称
#修改对象的属性
对象变量名.属性名称 = 新的属性值
#查看对象的属性(专属属性)
#无论是查看类的属性还是查找对象的属性,都是使用__dict__
#查看对象的类,使用__class__
class Person:
eat = '会吃饭'
run = '会跑路'
print(Person.__dict__) #类的属性
res = Person()
print(res) #没有专属属性的对象
print(res.__dict__)
print(res.__class__) #对象的类信息
zhangsan = Person()
zhangsan.eat = '不会吃饭'
print(zhangsan.__dict__) #有专属属性的对象
print(zhangsan.__class__) #对象的类信息
{'__module__模块名称': '__main__', 'eat': '会吃饭', 'run': '会跑路', '__dict__': <attribute '__dict__' of 'Person' objects>, '__weakref__': <attribute '__weakref__' of 'Person' objects>, '__doc__注释': None}
<__main__.Person object at 0x000001C11D14A9E8>
{}
<class '__main__.Person'>
{'eat': '不会吃饭'}
<class '__main__.Person'>
初始化对象的属性
#使用类产生对象1
class Teacher:
school = '清华'
t1 = Teacher()
t1.name = 'tom'
t1.age = 19
t2 = Teacher()
t2.name = 'jerry'
t2.age = 20
t3 = Teacher()
t3.name = 'syy'
t3.age = 21
print(t1.name)
print(t2.name)
print(t3.name)
#使用类产生对象2,使用函数封装重复代码
class Teacher:
school = '清华'
def init(obj,name,age):
obj.name = name
obj.age = age
t1 = Teacher()
t2 = Teacher()
t3 = Teacher()
init(t1,'tom',19)
init(t2,'jerry',19)
init(t3,'syy',19)
print(t1.name)
print(t2.name)
print(t3.name)
#使用类产生对象3,使用__init__方法
#self就是生成的对应的对象
#使用__init__定义的变量,就是对象的专属属性(__init__的作用就是给对象附初始值)
#函数名必须是__init__,__init__函数不能使用return返回值
#调用函数的时候,注意变量的个数要对应类体内的变量的个数(少一个),原因在于,调用函数的时候会将对象本身作为第一个参数
#当需求是在创建对象时还需要做掉别的事,那就应该想到对象的初始化
class Teacher:
school = '清华'
def __init__(self):
print(self)
t1 = Teacher()
print(t1)
<__main__.Teacher object at 0x000002C04478F908>
<__main__.Teacher object at 0x000002C04478F908>
class Teacher:
school = '清华'
def __init__(self,name,age):
self.name = name
self.age = age
t1 = Teacher('tom',19)
t2 = Teacher('jerry',20)
t3 = Teacher('syy',21)
print(t1,t2,t3)
print(t1.name,t2.name,t3.name)
<__main__.Teacher object at 0x0000022248F15A90> <__main__.Teacher object at 0x0000022248F15BA8> <__main__.Teacher object at 0x0000022248F159B0>
tom jerry syy
#使用locals()初始化对象
#locals(),是一个字典,表示当前名称空间中所有的变量名
class Hero:
def __init__(self,name,level,blood,attach,q_hurt,w_hurt,e_hurt):
# self.name = name
# self.level = level
# self.blood = blood
# self.attach = attach
# self.q_hurt = q_hurt
# self.w_hurt = w_hurt
# self.e_hurt = e_hurt
lcs = locals()
lcs.pop('self')
self.__dict__.update(lcs)
s1 = Hero(1,2,3,4,5,6,7)
print(s1.__dict__)
{'e_hurt': 7, 'w_hurt': 6, 'q_hurt': 5, 'attach': 4, 'blood': 3, 'level': 2, 'name': 1}
对象的绑定方法
#对象绑定方法
#不使用装饰器的前提下,类中的方法(函数),都是对象绑定方法
#使用类生成对象,通过对象可以调用别的方法(函数体代码)
#一个类里面可以有多个函数,但是一个类同时只能生成一个对象
#只要一生成对象,类中的代码就会运行,但是不会运行除了__init__别的函数体代码
#别的函数体代码只会在调用的时候运行
class Student:
school = '清华'
def __init__(self,name,age):
self.name = name
self.age = age
print(self)
def study(self):
print('hello i am a student, my name is:%s'%self.name)
t1 = Student('syy',18)
t2 = Student('tom',19)
print(t1.study()) #不需要传参,自动将该对象当做第一个参数传入
print(t2.study())
<__main__.Student object at 0x000002842EECAA20>
<__main__.Student object at 0x000002842EEDE978>
hello i am a student, my name is:syy
None
hello i am a student, my name is:tom
None
#使用对象调用方法和使用类调用方法的行为不同
#使用对象调用方法,有几个参数就传几个参数
#使用类名调用方法,该方法就是一个普通函数
class Student:
school = '清华'
def __init__(self,name,age):
self.name = name
self.age = age
def study(self):
print('hello i am a student, my name is:%s'%self.name)
t1 = Student('syy',18)
t1.study() #使用对象调用方法
Student.study(t1) #使用类名调用方法
hello i am a student, my name is:syy
hello i am a student, my name is:syy
#练习
#写一个学生类,具备一个打招呼的技能,要能输出自己的名字信息
class Student:
def __init__(self,name):
self.name = name
def say_hi(self):
print('my name is %s'%self.name)
s1 = Student('syy')
s1.say_hi()
my name is syy
类的绑定方法
#
class Student:
school = '清华'
def __init__(self,name,age):
self.name = name
self.age = age
def study(self):
print(self.school)
t1 = Student('syy',18)
print(Student.school) #访问类
print(t1.school) #访问对象
Student.study(t1) #访问类中的函数(需要传入一个对象)
t1.study()
#类的绑定方法
#使用装饰器@classmethod装饰
#不管是用类还是对象调用,都会自动传入类本身,作为第一个参数
class Student:
school = '清华'
def __init__(self,name,age):
self.name = name
self.age = age
@classmethod
def study(cls):
print(cls.school)
t1 = Student('syy',18)
print(Student.school)
print(t1.school)
Student.study() #访问类中的函数(不需要传参数)
t1.study()
#什么时候绑定给对象
#当函数逻辑需要访问对象中的数据时
#什么时候绑定给类
#当函数逻辑需要访问类中的数据时
非绑定方法
#非绑定方法或叫做静态方法
#使用装饰器@staticmethod
#即类既不绑定对象self,也不绑定类cls
#该方法定义函数与类体外定义函数结果相同
class Student:
school = '清华'
def __init__(self,name,age):
self.name = name
self.age = age
@staticmethod
def study():
print('hello')
t1 = Student('syy',18)
Student.study() #使用类访问方法
t1.study() #使用对象访问方法
#实例
#为学生类添加一个save方法,一个get方法
#save是将对象存储到文件中(对象绑定方法)
#get是从你文件中获取对象(非绑定方法)
import pickle
class Student:
def __init__(self,name):
self.name = name
def save(self):
with open(self.name,'wb') as f:
pickle.dump(self,f)
@staticmethod
def get(name):
with open(name,'rb') as f:
obj = pickle.load(f)
return obj
s1 = Student('syy')
s1.save()
obj = Student.get('syy')
print(obj.name)
#当类中的函数需要使用self或者__init__中定义的变量,那么这个函数就是对象绑定
#当类中的函数需要使用类中定义的变量,那么这个函数就是类绑定方法,可以使用装饰器classmethod修饰
#当类中的函数既不需要使用类中定义的变量,又不需要使用__init__中定义的变量,那么这个对象就是非绑定方法
实例 (对象交互练习)
参考网站
import random
import time
class Hero:
def __init__(self,name,level,blood,att,Q_hurt,W_hurt,E_hurt):
lcs = locals()
lcs.pop('self')
self.__dict__.update(lcs)
def attack(self,enemy):
enemy.blood -= self.att
print('%s对%s释放了普通攻击,造成了%s的伤害,敌人剩余血量%s'%(self.name,enemy.name,self.att,enemy.blood))
if enemy.blood <= 0:
print('%s被%s使用普通攻击击杀!'%(enemy.name,self.name))
def Q(self,enemy):
enemy.blood -= self.Q_hurt
print('%s对%s释放了Q技能,造成了%s的伤害,敌人剩余血量%s'%(self.name,enemy.name,self.Q_hurt,enemy.blood))
if enemy.blood <= 0:
print('%s被%s使用Q技能击杀!'%(enemy.name,self.name))
def W(self,enemy):
enemy.blood -= self.W_hurt
print('%s对%s释放了W技能,造成了%s的伤害,敌人剩余血量%s'%(self.name,enemy.name,self.W_hurt,enemy.blood))
if enemy.blood <= 0:
print("%s被%s使用W技能击杀!" % (enemy.name, self.name))
def E(self,enemy):
enemy.blood -= self.E_hurt
print('%s对%s释放了E技能,造成了%s的伤害,敌人剩余血量%s'%(self.name,enemy.name,self.E_hurt,enemy.blood))
if enemy.blood <= 0:
print('%s被%s使用E技能击杀!'%(enemy.name,self.name))
#定义英雄
h1 = Hero('亚索',level=1,blood=50000,att=500,Q_hurt=600,W_hurt=1000,E_hurt=2000)
h2 = Hero('妲己',level=2,blood=3000,att=600,Q_hurt=700,W_hurt=1000,E_hurt=2000)
h3 = Hero('鲁班',level=3,blood=3000,att=700,Q_hurt=800,W_hurt=1500,E_hurt=2000)
h4 = Hero('蔡文姬',level=4,blood=5000,att=100,Q_hurt=200,W_hurt=300,E_hurt=400)
# h1.attack(h2)
# h2.Q(h1)
# h2.W(h1)
# h2.E(h1)
while True:
#把所有的攻击方法装到字典,为了随机取出一个
funcs = {1:Hero.Q,2:Hero.W,3:Hero.E,4:Hero.attack}
func_index = random.randint(1,len(funcs))
func = funcs[func_index]
#把所有英雄装到字典,为了随机取出一个
heros = {1:h1,2:h2,3:h3,4:h4}
hero_index = random.randint(1,len(heros))
hero = heros[hero_index]
#剩余的英雄们
other_heros = {}
new_index = 1
for k,v in heros.items():
if k != hero_index:
other_heros[new_index] = v
new_index +=1
#从剩余的英雄中随机跳出一个英雄来挨打
enemy_index = random.randint(1,len(other_heros))
enemy = other_heros[enemy_index]
#打他
func(hero,enemy)
if enemy.blood <=0:
break
time.sleep(1)
面向对象(OOP)的三大特征
继承
#什么是继承
继承是一种关系,描述两个类之间的关系
在程序中,继承描述的是类和类之间的关系(父类、基类、子类)
在python中,一个子类可以同时继承多个父类,使用逗号分开
#为什么要使用继承
继承的一方可以直接使用被继承一方已经有的东西
继承的作用就是重用已经有的代码,提高重用性
#如何使用继承
#语法
class 类名称(父类的名称):
pass
#例
class Base:
desc = '这是一个基类'
def show_info(self):
print(self.desc)
def make_money(self):
print('一天赚它一个亿')
class Subclass(Base):
pass
# def make_money(self):
# print('一天赚它一百块')
obj = Subclass()
obj.make_money()
print(obj.desc)
一天赚它一个亿
这是一个基类
抽象与继承
#原始代码
class Teacher:
def __init__(self,name,age,gender):
self.name = name
self.age = age
self.gender = gender
def say_hi(self):
print('name:%s,age:%s,gender:%s'%(self.name,self.age,self.gender))
class Student:
def __init__(self,name,age,gender):
self.name = name
self.age = age
self.gender = gender
def say_hi(self):
print('name:%s,age:%s,gender:%s'%(self.name,self.age,self.gender))
t1 = Teacher('syy','male',20)
t1.say_hi()
stu1 = Student('dog','female',2)
stu1.say_hi()
#抽象
#生活中,抽象是指不具体、不清晰、很模糊、看不懂,形容词
#程序中,抽象是动词
#将多个不同的子类中相同的部分进行抽取,形成一个新的类,这个过程叫抽象
#先抽象再继承
#继承一个已经存在的类,可以扩展或是修改原始的功能
class Person:
def __init__(self,name,age,gender):
self.name = name
self.age = age
self.gender = gender
def say_hi(self):
print('name:%s,age:%s,gender:%s'%(self.name,self.age,self.gender))
class Teacher(Person):
def teacher(self):
print('老师专属...')
class Student(Person):
pass
t1 = Teacher('syy','male',20)
t1.say_hi()
stu1 = Student('dog','female',2)
stu1.say_hi()
属性查找顺序
#属性查找顺序
对象 > 子类 > 父类
class A:
a = 1
class B(A):
b = 2
def __init__(self,c):
self.c = c
class C(B):
pass
class D(C):
pass
t = B(3)
print(t.b) #2
print(t.a) #1
print(t.c) #3
print(D.mro()) #所有父类名,按继承顺序排列,[<class '__main__.D'>, <class '__main__.C'>, <class '__main__.B'>, <class '__main__.A'>, <class 'object'>]
派生与覆盖
#派生
当一个子类中出现了与父类中不同的内容时,这个子类就称之为派生类
通常子类都会写一些新的代码,不可能和父类完全一样,所以,子类通常都是派生类
#覆盖
覆盖也称之为overwrite
当子类出现了与父类完全一致(名字一致)的属性和方法时,父类中的属性和方法被子类覆盖
#派生与覆盖
class Person:
def say_hi(self):
print('hello person')
class Student(Person):
def say_hi(self):
print('hello student')
stu = Student()
print(stu.say_hi())
#例
实现一个可以限制元素类型的列表
#子类中访问父类的内容使用关键字super
class Mylist(list):
def __init__(self,element_type):
#先继承父类,后自定义子类的初始化
super().__init__()
#element_type: 指定的数据类型
self.element_type = element_type
def append(self,object):
#object: 要存储的元素
if type(object) == self.element_type:
#在这里访问父类的append函数
super(Mylist,self).append(object)
else:
print('sorry,you element type not is %s'%self.element_type)
m = Mylist(str)
m.append('壹')
print(m[0])
super
参考网站
#python2中的supper
class Parent:
test = 'abc'
def say_something(self):
print('anything')
class Sub(Parent):
def show_info(self):
print(super(Sub,self).test)
super(Sub, self).say_something()
sub = Sub()
sub.show_info()
#python3中的supper
class Parent:
test = 'abc'
def say_something(self):
print('anything')
class Sub(Parent):
def show_info(self):
print(super().test)
super().say_something()
sub = Sub()
sub.show_info()
#使用类名称,与继承无关
class Parent:
test = 'abc'
def say_something(self):
print('anything')
class Sub(Parent):
def show_info(self):
print(Parent.test)
Parent.say_something(self)
sub = Sub()
sub.show_info()
#参考
class Animal: # 定义一个父类
def __init__(self): # 父类的初始化
self.name = 'animal'
self.role = 'parent'
print('I am father')
class Dog(Animal): # 定一个继承Animal的子类
def __init__(self): # 子类的初始化函数,此时会覆盖父类Animal类的初始化函数
super(Dog, self).__init__() # 在子类进行初始化时,也想继承父类的__init__()就通过super()实现,此时会定义self.name= 'animal'等
print('I am son')
self.name = 'dog' # 定义子类的name属性,并且会把刚才的self.name= 'animal'更新为'dog'
animal = Animal() #I am father
xbai = Dog() #I am son(没有继承父类的print?)
print(xbai.name) #'dog'
#使用super继承
#子类在初始化的时候,调用父类的初始化
class Teacher:
def __init__(self,name,age,gender):
self.name = name
self.age = age
self.gender = gender
def say_hi(self):
print('name: %s,age: %s,gender: %s'%(self.name,self.age,self.gender))
class Student(Teacher):
def __init__(self,name,age,gender,number):
super(Student, self).__init__(name,age,gender)
self.number = number
def say_hi(self):
super().say_hi()
print('number: %s'%self.number)
stu = Student('syy',18,'male',2333333)
stu.say_hi()
name: syy,age: 18,gender: male
number: 2333333
#继承方法
class Teacher:
def __init__(self,name,age,gender):
self.name = name
self.age = age
self.gender = gender
self.aa()
def say_hi(self):
print('name: %s,age: %s,gender: %s'%(self.name,self.age,self.gender))
def aa(self):
print('my name si aa')
class Student(Teacher):
def __init__(self,name,age,gender,number):
super().__init__(name,age,gender)
self.number = number
def say_hi(self):
super().say_hi()
print('number: %s'%self.number)
stu = Student('syy',18,'male',2333333)
stu.say_hi()
#当继承一个现有的类,并且覆盖了父类的__init__方法,那么,必须在初始化方法的第一行调用父类的初始化方法,并传入父类所需的参数,再根据情况自定义属性
组合
#组合
组合描述的是对象与对象之间的关系
将一个对象作为另一个对象的属性
组合也可以理解成继承的一种
组合对比继承,类的耦合度更低
#组合的作用
重用代码,减少代码冗余
#什么时候使用继承
两个类之间存在父子关系,同类
#什么时候使用组合
两个类之间毫无关系,异类
#组合的代码实现
#人不是收集,但是人可以使用收集的功能
class Phone:
def __init__(self,price,kind,color):
self.price = price
self.kind = kind
self.color = color
def call(self):
print('呼叫...')
def send_message(self):
print('发短信...')
class Student:
def __init__(self,name,age,phone):
self.name = name
self.age = age
self.phone = phone
def show_info(self):
print('name: %s,age: %s'%(self.name,self.age))
phone1 = Phone(10000,'apple','red')
stu1 = Student('syy',18,phone1)
stu1.phone.call()
菱形继承
#python支持多继承
存在类的继承顺序问题
#一般的继承顺序
#按Sub的书写顺序继承
class A:
pass
class B:
pass
class C:
pass
class Sub(A,B,C):
pass
print(Sub.mro())
[<class '__main__.Sub'>, <class '__main__.A'>, <class '__main__.B'>, <class '__main__.C'>, <class 'object'>]
#python3中,任何类都是直接或间接的继承Object
class A:
pass
print(A.mro()) #[<class '__main__.A'>, <class 'object'>]
#新式类与经典类
#新式类
任何显示或隐式的继承自Object的类,就叫做新式类
python3中,全部都是新式类
#隐式类
没有继承Object的类,就叫做经典类,python2中才会存在经典类
所以类的创建一般标识继承自object,这样代码既可以兼容python2有可以兼容python3
#菱形继承
当一个类有多个父类,且多个父类有共同的基类,那么这个类的继承就叫做菱形继承
class A:
j = 1
pass
class B(A):
# j = 2
pass
class C(A):
j = 3
pass
class D(B,C):
# j =4
pass
test = D.j
print(test) #3
#菱形继承中新式类与经典类的区别
#新式类
有共同父类就广度(广度没有再深度),没有共同父类就深度找
#经典类
有共同父类是深度(深度没有再广度),没有共同父类是且仅是深度找
class A:
num = 1
pass
class B():
num = 2
pass
class C(A):
num = 3
pass
class D(A):
num = 4
pass
class E(B):
# num = 5
pass
class F(C):
num = 6
pass
class G(D):
num = 7
pass
class H(E,F,G):
# num = 8
pass
print(H.num) #2
print(H.mro()) #[<class '__main__.H'>, <class '__main__.E'>, <class '__main__.B'>, <class '__main__.F'>, <class '__main__.C'>, <class '__main__.G'>, <class '__main__.D'>, <class '__main__.A'>, <class 'object'>]
继承的使用
#原始数据
程序员,姓名、性别、年龄、工资、编程技能
项目经理,姓名、性别、年龄、工资、编程技能、奖金、管理技能
#分析关系
初始化变量较多的时候,可以使用locals
项目经理来自程序员,可以使用super继承,没有必要使用抽象
无论是程序员还是项目经理,都要使用存和取,单独抽取代码更加简洁,有利于代码扩展
import pickle
import os
class Baseclass:
'''
将存储数据的操作单独抽取,这样可以降低耦合度,减少代码冗余
'''
def save(self):
#判断类名对应的文件夹是否存在
cls_name = self.__class__.__name__
if not os.path.exists(cls_name):
os.makedirs(cls_name)
path = os.path.join(cls_name,self.name)
with open(path,'wb') as f:
pickle.dump(self,f)
@classmethod
def get_obj(cls,name):
#拼接路径
path = os.path.join(cls.__name__,name)
if os.path.exists(path):
with open(path,'rb') as f:
return pickle.load(f)
class Coder(Baseclass):
def __init__(self,name,age,gender,salary):
self.name = name
self.age = age
self.gender = gender
self.salary = salary
def programming(self):
print('程序员正在开发项目...')
class Manager(Coder):
def __init__(self,name,age,gender,salary,bonus):
super().__init__(name,age,gender,salary)
self.bonus = bonus
def manage(self):
print('经理正在殴打程序员...')
if __name__ == '__main__':
# cod1 = Coder('syy',18,'男神',10000)
# cod1.programming()
# cod1.save()
# man1 = Manager('ji',88,'糟老头子',100,1)
# man1.programming()
# man1.save()
cod1 = Coder.get_obj('syy') #type: Coder
cod1.programming()
man1 = Manager.get_obj('ji') #type: Manager
man1.manage()
程序员正在开发项目...
经理正在殴打程序员...
封装
#什么是封装
封装就是将复杂的细节隐藏到内部,对外提供简单的使用接口
#为什么要使用封装
为了保证关键数据的安全性
对外隐藏细节,隔离复杂度
#什么时候使用封装
当有一些数据不希望外界直接修改
当有一些数据不希望外界使用时
#怎么使用封装
class Person:
def __init__(self,id_number,name,age):
self.id_number = id_number
self.name =name
self.age = age
def show_id(self):
print(self.id_number)
p = Person('111','syy',18)
p.id_number = '222'
p.show_id() #222
print(p.id_number) #222
class Person:
def __init__(self,id_number,name,age):
self.__id_number = id_number
self.name =name
self.age = age
def show_id(self):
print(self.__id_number)
p = Person('111','syy',18)
p.__id_number = '222'
p.show_id() #111
print(p.__id_number) #222
class Person:
def __init__(self,id_number,name,age):
self.__id_number = id_number
self.name =name
self.age = age
def show_id(self):
print(self.__id_number)
p = Person('111','syy',18)
p.id_number = '222'
p.show_id() #111,此时外界无法修改类体(可以使用_Person__id_number修改)
print(p.id_number) #222
#例
class PC:
def __init__(self,price,kind,color):
self.price = price
self.kind = kind
self.color = color
def open(self):
print('接通电源')
self.__check_device()
print('载入内核')
print('初始化内核')
self.__start_service()
print('启动GUI')
self.__login()
def __check_device(self):
print('硬件检测1.')
print('硬件检测2..')
print('硬件检测3...')
def __start_service(self):
print('启动服务1.')
print('启动服务2..')
print('启动服务3...')
def __login(self):
print('登录1.')
print('登录2..')
print('登录3...')
p = PC(10000,'苹果','白色')
p.open()
# p.login(),外界无法修改类体,无法使用类体内部的数据
#被封装的函数的特点
1.外界不能直接访问
2.内部依然可以使用函数名调用
#学习了封装之后就可以控制属性的权限
1.公开权限,默认所有属性和方法都是公开的
2.私有权限,当前类才能使用的属性和方法
#如何访问被封装的属性或方法
#属性虽然被封装了,但是还是需要访问或修改的
#通过定义函数,完成对私有属性的访问或修改
#这样以来,可以在外界访问或修改私有属性或方法时,添加额外的逻辑
class Downloader:
def __init__(self,filename,url,buffer_size):
self.filename = filename
self.url = url
self.__buffer_size = buffer_size
def start_download(self):
if self.__buffer_size <= 1024*1024:
print('当前缓冲大小为: %s'%self.__buffer_size)
print('开始下载...')
else:
print('内存炸了!!!')
#定义接口函数
def set_buffer_size(self,size):
#可以在接口函数中添加额外的逻辑
if type(size) == int:
print('缓冲大小修改成功,大小为: %s'%size)
self.__buffer_size = size
else:
print('缓冲大小必须是整型!!!')
# 通过函数访问内部封装的属性
def get__buffer_size(self):
return self.__buffer_size
#生成对象
d = Downloader('奥特曼全集','www.aoteman.com',1024*1024)
#下载
d.start_download()
#修改函数内部封装的属性
d.set_buffer_size(1024*512)
#继续下载
d.start_download()
property、key.setter、key.deleter装饰器
#装饰器是用在函数上的
#property装饰器
该装饰器为了解决封装的属性和方法的'访问'方法的不同
使用了该装饰器,类中隐藏方法的'访问',直接使用点,不再使用括号
#key.setter装饰器
该装饰器为了解决封装前后的属性'修改’的不同
使用了该装饰器,类中隐藏属性的修改,直接使用点,不再使用括号
#key.deleter装饰器
该装饰器为了解决不能删除封装的属性的问题,类似于触发器
使用了该装饰器,类中隐藏属性的删除,可以直接删除,也可以设置触发条件
#原始代码
class A:
def __init__(self,name,key):
self.name = name
self.__key = key
def get_key(self):
return self.__key
def set_key(self,new_key):
self.__key = new_key
a = A('syy',123)
print(a.name) #访问属性不加括号
print(a.get_key()) #访问方法加括号
#使用了property后的代码
class A:
def __init__(self,name,key):
self.name = name
self.__key = key
@property
def get_key(self):
return self.__key
def set_key(self,new_key):
self.__key = new_key
a = A('syy',123)
print(a.name) #访问属性不加括号
print(a.get_key) #访问方法不再需要加括号,隐藏属性与普通属性访问方式相同
a.set_key(456) #属性的修改需要加括号
print(a.get_key)
#使用property、key.setter、key.deleter后的代码
class A:
def __init__(self,name,key):
self.name = name
self.__key = key
@property
def key(self):
return self.__key
@key.setter
def key(self,new_key):
self.__key = new_key
@key.deleter
def key(self):
print('不允许删除该属性...')
#del self.__key #可以在这里删除隐藏属性
a = A('syy',123)
print(a.key)
a.key = 456 #属性的修改不需要再使用括号,直接点,隐藏属性与普通属性修改方式相同
print(a.key)
del a.key #隐藏属性不能直接删除,可以使用装饰器删除,隐藏属性与普通属性删除方式相同
print(a.key)
123
456
不允许删除该属性...
456
#注意
key是property装饰器装饰的方法的名字,在使用setter和deleter时,装饰器的函数名要保持一致
封装的原理
#封装的原理
#python中,类中的属性的封装非常简单,仅仅是把类体中的属性的'__变量名'改为'_类名__变量名',这样外界将不能直接使用变量名或者__变量名访问隐藏属性(但是外界可以使用_类名__变量名访问类的隐藏属性)
#字符串的修改,对于python解释器的是很简单的
#这种字符串的修改只会在加载类的时候,在类体中运行(属性隐藏和访问的时候,'添加都会运行')
class A:
def __init__(self,key):
self.__key = key
@property
def key(self):
return self.__key
@key.deleter
def key(self):
del self.__key
a = A(123)
print(a.key)
del a.key
print(a.key) #AttributeError: 'A' object has no attribute '_A__key'
#
class A:
def __init__(self,key):
self.__key = key
@property
def key(self):
return self.__key
@key.deleter
def key(self):
del self.__key
a = A(123)
print(a.key) #123
print(a.__dict__) #{'_A__key': 123}
print(a._A__key) #123
计算属性
#使用property装饰器,可以用来实现计算属性
#计算属性指的是:属性的值不能直接获取,必须通过计算才能获取
#这里的property装饰器,仅仅只是为了让方法的调用不加括号,与属性的调用相同而已
#例
求正方形的面积
class Square:
def __init__(self,width):
self.width = width
@property
def area(self):
return self.width * self.width
s = Square(10)
print(s.area) #100
s.width = 20
print(s.area) #400
#例2
求BMI,BMI = 体重/身高的平方
class Bmi:
def __init__(self,height,weight):
self.height = height
self.weight = weight
@property
def bmi(self):
return self.weight/self.height/self.height
b = Bmi(170,165)
print(b.bmi) #0.005709342560553633
接口
#什么是接口
接口是一组功能的集合,但是接口中只包含功能的名字,不包含具体的实现代码
接口本质上是一套协议标准,遵循这个标准的对象就能被调用
#接口的作用
为了提高代码的扩展性
#例
例如电脑上的USB接口协议,只要你遵循该协议,相关的设备就能被电脑使用,不需要关心设备的种类
#PC的代码一旦完成,后期无论什么样的设备,只要遵循了USB接口协议,都能被电脑调用
#接口主要是方便了对象的使用者,降低了使用者的学习难度,因为使用者只要学习了一套使用方法,就可以操作各类USB设备
class USB:
def open(self):
pass
def close(self):
pass
def read(self):
pass
def write(self):
pass
class Mouse(USB):
def open(self):
print('鼠标开机')
def close(self):
print('鼠标关机')
def read(self):
print('获取光标位置')
def write(self):
print('鼠标不支持写入')
class Keyboard(USB):
def open(self):
print('键盘开机')
def close(self):
print('键盘关机')
def read(self):
print('获取键盘字符')
def write(self):
print('键盘写入灯光颜色')
def pc(usb_device):
usb_device.open()
usb_device.read()
usb_device.write()
usb_device.close()
m = Mouse()
k = Keyboard()
pc(m)
pc(k)
鼠标开机
获取光标位置
鼠标不支持写入
鼠标关机
键盘开机
获取键盘字符
键盘写入灯光颜色
键盘关机
#问题
如果子类没有按照你的协议来进行代码的编写,将导致代码无法运行
抽象类
#abc模块,是单词absract class的缩写,意为'抽象类'
#抽象类
一个类中没有包含函数体(或者经过装饰器装饰的函数体),那么这个类就是抽象类
#作用
可以限制子类必须遵循父类中定义的抽象方法
import abc
class USB(metaclass=abc.ABCMeta):
@abc.abstractmethod
def open(self):
pass
@abc.abstractmethod
def close(self):
pass
@abc.abstractmethod
def read(self):
pass
@abc.abstractmethod
def write(self):
pass
class Mouse(USB):
def open(self):
print('鼠标开机')
def close(self):
print('鼠标关机')
def read(self):
print('获取光标位置')
def write(self):
print('鼠标不支持写入')
class Keyboard(USB):
def open(self):
print('键盘开机')
def close(self):
print('键盘关机')
def read(self):
print('获取键盘字符')
def write(self):
print('键盘写入灯光颜色')
def pc(usb_device):
usb_device.open()
usb_device.read()
usb_device.write()
usb_device.close()
m = Mouse()
k = Keyboard()
pc(m)
pc(k)
鸭子类型
#python一般不会限制你必须怎么写,作为一个优秀的程序员,就应该遵守相关协议,所以有了鸭子类型的说法
#鸭子类型
如果一个对象长得像鸭子,走路像鸭子,那他就是鸭子
#由此类推,只要保证你的类,按照相关的协议编写,就可以达到提高扩展性的目的
class Mouse:
def open(self):
print('鼠标开机')
def close(self):
print('鼠标关机')
def read(self):
print('获取光标位置')
def write(self):
print('鼠标不支持写入')
class Keyboard:
def open(self):
print('键盘开机')
def close(self):
print('键盘关机')
def read(self):
print('获取键盘字符')
def write(self):
print('键盘写入灯光颜色')
def pc(usb_device):
usb_device.open()
usb_device.read()
usb_device.write()
usb_device.close()
m = Mouse()
k = Keyboard()
pc(m)
pc(k)
多态性
#多态
生活中,一种事物有多种不同的形态
官网:多个对象可以响应同一个方法,产生不同的结果
多态不是一种特殊的语法,而是一种状态、特性
接口、抽象类、鸭子类型,都可以写出具备多态的代码,其中最简单的就是鸭子类型
#优点
对于使用者而言,大大的降低了使用难度,例如USB接口程序就有多态性
#例1
class Ji:
def bark(self):
print('咯咯咯...')
def spawn(self):
print('下鸡蛋')
class Ya:
def bark(self):
print('嘎嘎嘎...')
def spawn(self):
print('下鸭蛋')
class E:
def bark(self):
print('eee...')
def spawn(self):
print('下鹅蛋')
j = Ji()
y = Ya()
e = E()
#多个对象使用相同的方法,可以得到不同的结果
def manage(obj):
obj.spawn()
manage(j)
manage(y)
manage(e)
下鸡蛋
下鸭蛋
下鹅蛋
#例2
a = 10
b = '10'
c = [10]
print(type(a)) #多个变量名使用相同的方法,得到不同的结果
print(type(b))
print(type(c))
OOP相关的内置函数
#该内置函数无论在不在类中,都能执行
#代码1
def add_num(a,b):
if type(a) == int and type(b) == int:
return a+b
return None
print(add_num(1,2))
print(add_num(1,'2'))
#isinstance()
使用isinstance()函数,判断数据类型
isinstance(对象,数据类型)
def add_num(a,b):
if isinstance(a,int) and isinstance(b,int):
return a+b
return None
print(add_num(1,2))
print(add_num(1,'2'))
#issubclass()
使用issubclass()函数,可以判断一个类是否为某个类的子类/子孙类
issubclass(子类,父类)
class Animal:
def eat(self):
print('动物要吃东西...')
class Pig(Animal):
def eat(self):
print('猪要吃猪食')
class Tree:
def light(self):
print('植物进行光和作用')
def manage(obj):
if issubclass(type(obj),Animal):
obj.eat()
else:
print('不是动物!')
p = Pig()
t = Tree()
manage(p)
manage(t)
类中的魔法函数
#__str__
只要是双下,就会在某个时候自动执行
__str__会在对象被转换为字符串时执行,转换的结果就是这个函数的返回值
#打印对象,得到内存地址
class Person:
pass
p = Person()
print(p)
<__main__.Person object at 0x0000028E1471FA20>
#打印对象,得到返回值
class Person:
def __str__(self):
return 'abc'
p = Person()
print(p) #abc
#python解释器中的内置变量、内置函数、内置类,在程序运行结束会自动清理,但是python解释器不会清理不属于解释器的资源,例如打开的文件等,这个时候就需要手动close关闭代码
#__del__,吸构函数,删除对象/类,得到返回值
#__del__函数只会执行一次
#执行时机:
手动删除对象时执行
程序运行结束,就会执行类中的__del__函数
#使用场景
当你的对象在使用过程中,打开了不属于python解释器的资源,例如文件、网络端口,在程序运行结束之后,就需要使用__del__函数来完成资源的释放工作
import time
class Person:
def __del__(self):
print('del run')
p = Person()
print(p)
# del p
time.sleep(2)
print('end')
<__main__.Person object at 0x0000025385DEF400>
end
del run
class Filetool:
'''该类用于文件的读取操作'''
def __init__(self,path):
self.file = open(path,'rt',encoding='utf-8')
def read(self):
return self.file.read()
#在这里可以确定生成的对象不再使用了,所以可以放心的关闭文件了
def __del__(self):
tool.file.close()
tool = Filetool(r'E:python_testa.txt')
print(tool.read()) #a.txt
#__init__,初始化函数、构造函数
class Person:
def __init__(self,name,age):
self.name = name
self.age = age
p = Person('syy',18)
print(p.name)
#__call__函数,在调用对象的时候执行(对象加括号)
class A:
def __call__(self, *args, **kwargs):
print('call run')
print(args)
print(kwargs)
a = A()
a(1,'壹',z=233)
#python是一门动态的语言,可以在代码运行期间动态的修改对象的属性所占用的空间
#如果代码运行内存不足,那么python解释器就会开启更大的内存区域,将原始的属性迁移过去
#这里存在一个问题,如果python解释器开启内存太大,那么将会造成内存的浪费
#解决方法是,在创建对象时,告诉python解释器要开启多大的内存,而不是让python解释器被动的开启内存
#__slots__函数,该属性是一个类属性,用于优化对象内存,将原本不固定的属性数量变得固定,这样python解释器就不会为这个对象创建名称空间,所以也没了__dict__方法
#使用了该函数,对象将不能再添加、删除属性
#代码1
import sys
class Person:
def __init__(self,name):
self.name = name
p = Person('syy')
print(sys.getsizeof(p)) #56
#代码2
import sys
class Person:
__slots__ = ['name']
def __init__(self,name):
self.name = name
p = Person('syy')
print(sys.getsizeof(p)) #48
# p.age = 18 #AttributeError: 'Person' object has no attribute 'age'
# print(p.__dict__) #AttributeError: 'Person' object has no attribute 'age'
属性的getattr、setattr、delattr函数
#这几个函数体现了python解释器如何使用点来设置、访问、删除属性的
#使用点设置属性的时候,执行__setattr__函数
#使用点访问属性(属性不存在)的时候,执行___getattr__函数
class A:
def __setattr__(self, key, value):
print('__setattr__')
def __getattr__(self, item):
print('__getattr__')
def __delattr__(self, item):
print('__delattr__')
a = A()
a.name = 'syy'
print(a.name)
__setattr__
__getattr__
None
class A:
def __setattr__(self, key, value):
super().__setattr__(key,value)
print('__setattr__')
def __getattr__(self, item):
print('__getattr__')
def __delattr__(self, item):
print('__delattr__')
a = A()
a.name = 'syy' #通过点语法操作对象的属性(原理是使用__dict__)
print(a.name) #syy,变量设置成功,所以可以打印出syy
__setattr__
syy
a = A()
a.__dict__['name'] = 'syy' #通过__dict__设置属性
print(a.name) #syy
class A:
def __setattr__(self, key, value):
self.__dict__[key] = value
print('__setattr__')
def __getattr__(self, item):
print('__getattr__')
def __delattr__(self, item):
print('__delattr__')
a = A()
print(a.xxx) #__getattr__
#删除属性的时候,执行__delattr__函数
class A:
def __setattr__(self, key, value):
self.__dict__[key] = value
print('__setattr__')
def __getattr__(self, item):
print('__getattr__')
def __delattr__(self, item):
print('__delattr__')
a = A()
a.name = 'syy'
del a.name
print(a.name) #删除不掉,原因是因为__delattr__对应的函数没有删除的操作
__setattr__
__delattr__
syy
class A:
def __setattr__(self, key, value):
self.__dict__[key] = value
print('__setattr__')
def __getattr__(self, item):
print('__getattr__')
def __delattr__(self, item):
self.__dict__.pop(item)
print('__delattr__')
a = A()
a.name = 'syy'
del a.name
print(a.name) #删除成功
__setattr__
__delattr__
__getattr__
None
#访问属性的时候,无论有没有该属性都会执行__getattribute__函数,
class A:
def __getattr__(self, item):
print('__getattr__')
def __getattribute__(self, item):
print('__getattribute__')
a = A()
a.name = 'syy'
print(a.name)
print(a.xxx)
__getattribute__
None
__getattribute__
None
#__dict__方法的原理就是使用了__getattribute__函数
class A:
def __getattr__(self, item):
print('__getattr__')
def __getattribute__(self, item):
print('__getattribute__')
return self.__dict__[item]
a = A()
a.name = 'syy'
print(a.name)
print(a.xxx) #RecursionError: maximum recursion depth
#实际上python内部是先使用__getattribute__取值,如果取不到,再使用__getattr__
class A:
def __getattr__(self, item):
print('__getattr__')
def __getattribute__(self, item):
print('__getattribute__')
return super(A, self).__getattribute__(item)
a = A()
a.name = 'syy'
print(a.name)
print(a.xxx)
__getattribute__
syy
__getattribute__
__getattr__
None
属性的setitem、getitem、delitem
#这几个函数体现了python解释器如何使用[]来设置、访问、删除属性的
#设置属性的时候执行__setitem__
class A:
def __setitem__(self, key, value):
print('__setitem__')
def __getitem__(self, item):
print('__getitem__')
def __delitem__(self, key):
print('__delitem__')
a = A()
a['name'] = 'syy' #__setitem__
print(a.name) #AttributeError: 属性不存在
#访问属性的时候执行__getitem__
class A:
def __setitem__(self, key, value):
print('__setitem__')
self.__dict__[key] = value
def __getitem__(self, item):
print('__getitem__')
return self.__dict__[item]
def __delitem__(self, key):
print('__delitem__')
del self.__dict__[key]
a = A()
a['name'] = 'syy'
print(a['name'])
__setitem__
__getitem__
syy
#删除属性的时候执行__delitem__
class A:
def __setitem__(self, key, value):
print('__setitem__')
self.__dict__[key] = value
def __getitem__(self, item):
print('__getitem__')
return self.__dict__[item]
def __delitem__(self, key):
print('__delitem__')
del self.__dict__[key]
a = A()
a['name'] = 'syy'
print(a['name'])
del a['name']
print(a['name']) #KeyError: 'name'
__setitem__
__getitem__
syy
__delitem__
__getitem__
#例
如何让一个对象既支持点语法取值,也支持括号取值
#对象本身就支持括号[]设置、访问、删除值,所以只需要添加点语法即可
class Mydict(dict):
def __setattr__(self, key, value):
self[key] = value
def __getattr__(self, key):
return self.get(key)
def __delattr__(self, item):
del self[item]
m = Mydict()
m['name'] = 'syy'
print(m['name']) #syy
del m['name']
# print(m['name'])
m.name = 'zyy'
print(m.name) #zyy
del m.name
# print(m.name)
运算符重载
#当我们在使用某个符号时,python会为这个符号定义一个含义,同时调用对应的处理函数,当我们需要自定义对象的比较规则时,就可以在子类中覆盖 >、<、=、!=等方法
#self和other指的是两个对象
#类似于大于、小于,我们只需要实现一个即可,如果符号不同,python解释器会自动交换两个对象的位置
class Student:
def __init__(self,name,age,height):
self.name = name
self.age = age
self.height = height
def __gt__(self, other):
if self.height > other.height:
return True
else:
return False
def __lt__(self, other):
if self.height < other.height:
return True
else:
return False
def __eq__(self, other):
if self.name == other.name and self.age == other.age and self.height == other.height:
return True
else:
return False
def __ne__(self, other):
if self.name == other.name or self.age == other.age or self.height == other.height:
return True
else:
return False
s1 = Student('syy',18,180)
s2 = Student('zyy',80,140)
print(s1 > s2)
print(s1 < s2)
print(s1 == s2)
print(s1 != s2)
True
False
False
True
#代码简写
class Student:
def __init__(self,name,age,height):
self.name = name
self.age = age
self.height = height
def __gt__(self, other):
return self.height > other.height
def __lt__(self, other):
return self.height < other.height
def __eq__(self, other):
return self.name == other.name and self.age == other.age and self.height == other.height
def __ne__(self, other):
return self.name != other.name or self.age != other.age or self.height != other.height
s1 = Student('syy',18,180)
s2 = Student('zyy',80,140)
print(s1 > s2)
print(s1 < s2)
print(s1 == s2)
print(s1 != s2)
True
False
False
True
迭代器协议
#迭代器的判定方法
1.内置有__iter__方法
2.内置有__next__方法
#迭代器的作用
迭代器就是一种取值的工具
节省空间
#迭代器原理
class Myiter:
def __init__(self,num):
self.num = num
self.count = 0
def __iter__(self):
return self
def __next__(self):
self.count += 1
if self.count <= self.num:
return '哈哈'
else:
raise StopIteration
for i in Myiter(10):
print(i)
#生成器
class Myrange:
def __init__(self,start,end,step):
self.start = start
self.end = end
self.step = step
def __iter__(self):
return self
def __next__(self):
a = self.start
self.start += self.step
if a < self.end:
return a
else:
raise StopIteration
for i in Myrange(1,10,2):
print(i)
上下文管理
#上下文comtext
指的是一段话的意义,要参考当前的场景,即参考上下文
在python中,上下文可以理解为是一个代码区间、一个范围,例如with open,打开的文件尽在这个上下文中有效
涉及到的两个方法:
enter:
表示进入上下文,进入某个场景
exit:
表示退出上下文,退出某个场景
#使用open类的with,先执行__enter__函数,open内代码运行结束,最后执行__exit__函数
class Myopen:
def __enter__(self):
print('__enter__')
def __exit__(self, exc_type, exc_val, exc_tb):
print('__exit__')
print(exc_type, exc_val, exc_tb)
with Myopen() as m:
print('start')
# '1' + 1 #open内代码执行的过程中,如果中途代码异常,则之后的代码不再执行,但是立即__exit__函数仍会执行,包含错误的类型、错误的信息、错误的追踪信息
print('over')
__enter__
start
over
__exit__
None None None
class Myopen:
def __init__(self,path):
self.path = path
def __enter__(self):
self.file = open(self.path)
return self
def __exit__(self,exc_type, exc_val, exc_tb):
self.file.close()
return False #通过返回的状态码判断代码运行的过程中,有诶有错误或者错误有没有被处理
with Myopen('a.txt') as m:
print(m.file.read())
反射
#反射reflect
#什么是反射
其实reflect的意思是反省、自省的意思
反射指的是一个对象应该具备可以检测、修改、增加自身属性的能力
反射就是通过字符串操作属性
反射涉及到的四个内置函数(python解释器自带的),hasattr、getattr、setattr、delattr
这几个函数都是针对于对象的,对应于对象属性是否存在、获取、设置、删除
这几个函数实际上就是封装了__dict__等方法而已
#hasattr()函数
#判断一个属性属不属于某个对象
class Person:
def __init__(self,name,age,gender):
self.name = name
self.age = age
self.gender = gender
p = Person('syy',18,'male')
print(hasattr(p,'name')) #True
print(hasattr(p,'names')) #False
#getattr()函数
#获取对象的属性,可以设置默认返回值(属性不存在的时候返回)
class Person:
def __init__(self,name,age,gender):
self.name = name
self.age = age
self.gender = gender
p = Person('syy',18,'male')
if hasattr(p,'name'):
print(getattr(p,'name',None))
#setattr()函数
#为对象添加新的属性
class Person:
def __init__(self,name,age,gender):
self.name = name
self.age = age
self.gender = gender
p = Person('syy',18,'male')
setattr(p,'id','123')
print(p.id) #123
#delattr()函数
#删除对象的属性
class Person:
def __init__(self,name,age,gender):
self.name = name
self.age = age
self.gender = gender
p = Person('syy',18,'male')
delattr(p,'name')
print(p.name) #AttributeError:
反射的使用场景
反射实际上就是对属性的增删改查,但是如果直接使用内置的__dict__来操作的话,会显得语法繁琐,不好理解,所以python封装了4个函数(getattr、setattr、delattr、getattribute)
封装的另一个原因就是,如果对象不是我自己写的而是另一方提供的,那我就必须判断这个对象是否具备我需要的属性和方法
反射是框架的基石,因为框架的设计者,不可能提前知道你的对象到底是怎么设计的,所以你提供给框架的对象,必须通过判断验证之后,才能正常的使用,而判断验证就是反射要做的事情,所以说反射就是对__dict__的操作进行封装
面向对象不是只有python才有的概念,任何高级语言都有的
#需求
要实现一个用于处理用户的终端指令的小框架
所谓的框架,就是已经实现了最基础的构架,就是所有的项目都一样的部分
#framwork/myframwork.py
import framework.plugins
def run(plugin):
while True:
cmd = input('请输入命令>>>: ').strip()
if cmd == 'exit':
break
#判断对象是否具备处理这个指令的方法
if hasattr(plugin,cmd):
#去除对应的方法并执行
func = getattr(plugin,cmd)
func()
else:
print('不支持该指令,请重新输入...')
print('bay bay ...')
wincmd = framework.plugins.Wincmd()
linuxcmd = framework.plugins.Linuxcmd()
run(wincmd)
run(linuxcmd)
#framwork/plugins.py
class Wincmd:
def cd(self):
print('wincmd: 切换目录')
def delete(self):
print('wincmd: 删除')
def dir(self):
print('wincmd: 查看目录')
class Linuxcmd:
def cd(self):
print('linuxcmd: 切换目录')
def rm(self):
print('linuxcmd: 删除')
def ls(self):
print('linuxcmd: 查看目录')
#思考
上述代码写死了必须使用某个类,这是不合理的,因为框架式的设计者无法提前知道对方的类在什么地方,以及类名叫什么
所以我们应该为框架的使用者提供一个配置文件,要求框架的使用者将类的信息写入配置文件,然后框架自己去加载需要使用的模块
动态导入
#在框架设计中,我们不可能提前知道框架用户要提供的类的相关信息,所以需要动态导入
#动态导入需要使用importlib模块
#framework/myframwork.py
import importlib
import framework.settings
def run(plugin):
while True:
cmd = input('请输入命令>>>: ').strip()
if cmd == 'exit':
break
#判断对象是否具备处理这个指令的方法
if hasattr(plugin,cmd):
#取出对应的方法并执行
func = getattr(plugin,cmd)
func()
else:
print('不支持该指令,请重新输入...')
print('bay bay ...')
#框架根据配置文件拿到类的路径
path = framework.settings.class_path
#框架从配置文件,单独拿出模块路径和类名称
module_path,class_name = path.rsplit('.',1)
mk = importlib.import_module(module_path)
cls = getattr(mk,class_name)
obj = cls()
run(obj)
#settings.py
#settings为框架的配置文件
#框架之外的部分就由自定义对象来完成,框架值提供固定的格式
#作为框架的使用者,在配置文件中,手动指定要使用的类的路径和类名
class_path = 'libs.plugins.Wincmd'
#plugins.py
class Wincmd:
def cd(self):
print('wincmd: 切换目录')
def delete(self):
print('wincmd: 删除')
def dir(self):
print('wincmd: 查看目录')
class Linuxcmd:
def cd(self):
print('linuxcmd: 切换目录')
def rm(self):
print('linuxcmd: 删除')
def ls(self):
print('linuxcmd: 查看目录')
元类type与object的关系
#在python中,万物皆对象,当然类也是对象
#对象是由类实例化产生的,所以类必然也是由另一个类实例化产生的
#什么是元类
元类就是产生类的类
所有类的元类默认都是type,type都是小写字母
type是object的类型,同时,object又是type的超类
元类type -> object -> 类
元类type -> 类
class type(object):
pass
class Person:
pass
p = Person()
print(type(p))
print(type(Person))
print(type(type))
print(type(object))
<class '__main__.Person'> 对象p由Person类产生
<class 'type'> 对象Person由type类产生
<class 'type'>
<class 'type'>
class type1(object):
pass
t = type1()
print(type(t))
print(type(type1))
<class '__main__.type1'>
<class 'type'> #所有类的元类默认都是type
#一个类的三个基本组成
1.类的名字,字符串类型
2.类的父类们,是一个元组或列表
3.类的的名称空间,字典类型
#type类创建类与使用关键字class创建类等效(使用元类创建类的两种方式)
class type(object):
def __init__(cls, what, bases=None, dict=None):
pass
cls_obj = type('test', (), {})
print(cls_obj)
class test:
pass
print(test)
class test(metaclass=type):
pass
print(test)
<class '__main__.test'> #cls_obj既是类又是对象
<class '__main__.test'>
<class '__main__.test'>
#为什么要使用元类
高度'自定义'一个类,例如控制类的名字必须以大驼峰的方式书写
只要是直接继承了type类,那么这个类就变成了一个元类,再利用覆盖可以做出'指定的元类',从而'限制类'
因为不能修改python的源码,所以在使用元类的时候要自定义一个元类
#需求
#如何用代码实现类名必须以大驼峰书写
#思考
类也是对象,可以由元类创建
类创建对象的时候执行类中的__init__方法,元类创建类的时候,执行元类中的__init__方法
创建类的时候还有别的操作,想到__init__方法
所以,只需要找到创建类的类(元类),再覆盖其中的__init__方法,就可以达到需求
#定义一个元类
class Mytype(type):
def __init__(self,class_name,bases,dict):
super().__init__(class_name,bases,dict)
if not class_name.istitle():
raise Exception('请书写大驼峰!')
#为pig类指定元类为Mytype
#方式1
# class Pig(metaclass=Mytype):
# pass
#方式2
Mytype('Pig',(),{})
call方法的应用
#类实例化产生对象(类加括号)的时候,执行类中的__init__方法
#对象加括号执行的时候,调用类中的__call__方法
class Dog(type):
def __call__(self,*args,**kwargs):
print('call run')
d = Dog() #类也是对象,1.执行类中的__init__2.执行元类的__call__方法
d() #call run,1.执行类中的__call__
#类与元类中__init__,__call__的执行关系
class Mymete(type):
def __init__(self,name,bases,dict):
super().__init__(name,bases,dict) #先覆盖,后导入
print('元类init run')
def __call__(self,*args,**kwargs): #self为元类生成的类,*args为传入的位置参数,**kwargs为传入的关键字参数
print('元类call run')
# print(*args)
# print(**kwargs)
return super().__call__(*args,**kwargs) #必须使用return,作用是创建对象,再执行子类的初始化
class Dog(metaclass=Mymete): #此时执行元类的__init__
def __init__(self,name): #元类__call__有返回值super().__call__(*args,**kwargs),就会执行
self.name = name
print('init run')
def __call__(self,*args,**kwargs): #生成的对象家括号执行的时候执行
print('call run')
d = Dog('syy') #此时执行元类的__call__,实例化产生对象就是执行元类中的__call__
print(d)
d()
元类init run
元类call run
init run
<__main__.Dog object at 0x00000212E9A4A908>
call run
#元类中的__call__方法
当你调用类对象时,会自动执行元类中的__call__方法,并将这个类本身作为第一个参数传入,以及后面的一堆参数
覆盖元类中的__call__之后,这个类就无法产生对象,必须调用super().__call__来完成对象的创建,并将其作为返回值
当想要控制对象的创建过程,就覆盖父类(type)的__call__方法
当想要控制类的创建过程,就覆盖父类(type)的__init__方法
#想要把对象的所有属性变成大写
class Mymete(type):
def __call__(self, *args, **kwargs):
new_args = []
for i in args:
new_args.append(i.upper())
return super().__call__(*new_args,**kwargs)
class Person(metaclass=Mymete):
def __init__(self,name):
self.name = name
p = Person('syy')
print(p.name) #SYY
#要求创建对象时,必须以关键字参数形式来传参
#注意一旦覆盖了父类(type)的__call__,就必须调用返回父类(type)的__call__
class Mymete(type):
def __call__(self, *args, **kwargs):
if args:
raise Exception('不好意思,不允许使用位置参数')
# return super().__call__(*args,**kwargs) #1.创建对象,2.子类初始化
obj = object.__new__(self)
self.__init__(obj,*args,**kwargs)
return obj
class Person(metaclass=Mymete):
def __init__(self,name):
self.name = name
p = Person(name='syy')
print(p.name) #syy
#补充
当你创建类对象时,会首先执行元类中的__new__方法,拿到一个空对象,然后自动调用元类__init__,然后对这个类进行初始化操作
如果覆盖了该方法,则必须保证__new__方法必须有返回值且必须是类对象
__new__方法和__init__方法,都可以实现控制类的创建过程,init更简单
class Mymete(type):
def __new__(cls, *args, **kwargs):
print(cls) #元类自己
print(args) #创建类需要的几个参数,类名、基类、名称空间
print(kwargs) #空
# return super().__call__(cls, *args, **kwargs)
obj = type.__new__(cls,*args,**kwargs)
return obj
def __init__(self,a,b,c):
super().__init__(a,b,c)
class A(metaclass=Mymete):
pass
m = A()
print(m)
<class '__main__.Mymete'>
('A', (), {'__module__': '__main__', '__qualname__': 'A'})
{}
<__main__.A object at 0x000001F53C5AA940>
#习题
定义一个元类,功能是获取类中所有的属性和方法,将名字存放到一个列表中,再将列表作为类的属性,使得类可以调用attrs,来获取自己的所有内容名称
class Mymeta(type):
def __init__(self,name,bases,dict):
super().__init__(name,bases,dict)
attrs = [i for i in list(dict.keys()) if not i.startswith('__')]
self.attrs = attrs
class A(metaclass=Mymeta):
name = 'syy'
def info(self):
print('info')
def say(self):
print('say')
print(A.attrs) #['name', 'info', 'say']
单例设计模式
#模式
固定的格式
#设计模式
用于解决某种固定问题的套路
#单例
一个类只产生一个对象
为了节省空间,但一个类的所有对象的属性全部相同时,则没有必要创建多个类
#伪单例
#使用get_instance()方法,只会创建一个对象
#但是使用'类()'仍然可以创建多个对象
class Person():
def __init__(self,name,age):
self.name = name
self.age = age
def say_hi(self):
print('hello %s'%self.name)
#用于获取对象的方法
@staticmethod
def get_instance(name,age):
#判断该类是否已经创建过对象
if hasattr(Person,'obj'):
return getattr(Person,'obj')
obj = Person(name,age)
Person.obj = obj
print('new了一次')
return obj
# p = Person()
p = Person.get_instance('syy',18)
p2 = Person.get_instance('syy',18)
p3 = Person.get_instance('syy',18)
#单例
class Mymeta(type):
def __call__(self, *args, **kwargs):
if hasattr(self,'obj'):
return getattr(self,'obj')
obj = super().__call__(*args,**kwargs)
self.obj = obj
print('new了一次')
return obj
class Person(metaclass=Mymeta):
def __init__(self,name,age):
self.name = name
self.age = age
def say_hi(self):
print('hello %s'%self.name)
p = Person('syy',18)
p2 = Person('syy',18)
p3 = Person('syy',18) #new了一次
冒泡排序
#冒泡排序
从一个列表中,每次取出两个元素,比较大小并排序
冒泡排序中,一圈中排序的次数 = 列表中元素的个数 - 1 - 再减去圈数的索引
冒泡排序中,圈数 = 列表中元素的个数 - 1
#例
#第一圈
把列表[1,2,3,5],从大到小进行冒泡排序
#第一次排序,得到2的位置
[2,1,3,5]
#第二次排序,得到3的位置
[2,3,1,5]
#第二次排序,得到5的位置
[2,3,5,1]
#第二圈
#第一次排序,得到3的位置
[3,2,5,1]
#第二次排序,得到5的位置
[3,5,2,1]
#第三圈
#第一次排序,得到5的位置
[5,3,2,1]
#代码实现
l = [1,2,3,5]
for i in range(len(l)-1): #i 为圈数的索引(range出的)
for j in range(len(l)-1-i): #j 为循环的次数,即排序的次数(range出索引)
if l[j] < l[j+1]: #j 为排序的次数,range后可以作为冒泡排序的索引
l[j],l[j+1] = l[j+1],l[j]
print(l) #[5,3,2,1]