面向对象深入
目录
动静态方法
总的来说就是在类中定义的函数有多种特性。
动态方法
类中的方法,会做一些自动的操作,比如自动把对象作为第一个参数传进去。
类中直接定义函数
类中直接定义函数 这个函数默认绑定给对象 类调用有几个参数传几个 对象调用第一个参数就是对象自身
# 绑定给对象的方法
class Student:
school_name = '清华大学'
def func1(self):
print(f'{self}好好学习!')
# 这种函数用于使对象产生独有的功能
# 1.对象调用
std1 = Student()
std1.func1() # <__main__.Student object at 0x0000023663AB6670>好好学习! # 将对象自己传入函数
# 2.类调用
Student.func1('请') # 请好好学习! # 需要一个参数
被@classmethod修饰的函数
有两种情况:
1.此函数被类调用时,会自动将类作为第一个参数传入其中。
2.被对象调用时,会自动将产生该对象的类作为第一个参数传入其中。
ps:cls即class的缩写,隐喻传的是类。这个函数默认绑定给类。传的全是类。
# 绑定给类的方法
class Student:
school_name = '清华大学'
@classmethod
def func2(cls):
print('我是一个类:', cls)
# 1.类调用
Student.func2() # 我是一个类: <class '__main__.Student'>
# 2.对象调用
std2 = Student()
std2.func2() # 我是一个类: <class '__main__.Student'>
print(Student) # <class '__main__.Student'>
静态方法
静态方法就是在类中定义普普通通的函数,不会进行任何自动的操作。无论是类调用,还是对象调用,都要手动传参。
被@staticmethod装饰的函数
# 静态方法
class Student:
school_name = '清华大学'
@staticmethod
def func3(a,b,c):
print('静态方法',a,b,c)
# 1.类调用
Student.func3(1,2,3) # 静态方法 1 2 3
# 2.对象调用
std2 = Student() # 静态方法 4 5 6
std2.func3(4,5,6)
面向对象之继承
"""
面向对象三大特性
封装 继承 多态
1.三者中继承最为核心(实操最多 体验最强)
2.封装和多态略微抽象
"""
1.继承的含义
在现实生活中继承表示人与人之间资源的从属关系
eg:儿子继承父亲 干女儿继承干爹
在编程世界中继承表示类与类之间资源的从属关系
eg:类A继承类B
2.继承的目的
在现实生活中儿子继承父亲就拥有了父亲所有资源的支配权限
在编程世界中类A继承类B就拥有了类B中所有的数据和方法使用权限
为了用!获得类中所有的数据和方法的使用权限
3.继承的实操
class Son(Father):
pass
1.在定义类的时候类名后面可以加括号填写其他类名 意味着继承其他类
2.在python支持多继承 括号内填写多个类名彼此逗号隔开即可
class Son(F1, F2, F3):
pass
"""
1.继承其他类的类 Son
我们称之为子类、派生类
2.被继承的类 Father F1 F2 F3
我们称之为父类、基类、超类
ps:我们最常用的就是子类和父类
"""
实操
单继承
class Father:
money = 666666
def play(self):
print('随便花')
class Son(Father):
pass
# 1.子类
print(Son.money) # 666666
# 2.子类产生的对象
grandson = Son()
print(grandson.money) # 666666
# 不仅继承的类(son)能啃老 继承的类的子孙后代都可以啃老
# 类的继承可以 让对象有多个查找数据的地方
多继承
class F1:
money = 666666
def play(self):
print('随便花')
class F2:
house = 666666
def play(self):
print('随便住')
class F3:
car = 666666
def play(self):
print('随便开')
class Son(F1, F2, F3):
pass
print(Son.money) # 666666
print(Son.house) # 666666
print(Son.car) # 666666
继承的本质
什么时候考虑用继承?
比如我们创建一个学生类和一个老师类:
class Student:
def __init__(self, name, age, gender):
self.name = name
self.age = age
self.gender = gender
def choice_course(self):
print(f'{self.name}正在选课')
class Teacher:
def __init__(self, name, age, gender):
self.name = name
self.age = age
self.gender = gender
def teach_course(self):
print(f'{self.name}正在教课')
可以发现代码中有重复的部分! 名字 年龄 性别 这是每个人都有的属性
因此我们可以创建一个'人类' 让老师和学生继承!
class Person:
def __init__(self, name, age, gender):
self.name = name
self.age = age
self.gender = gender
class Student(Person):
def choice_course(self):
print(f'{self.name}正在选课')
class Teacher(Person):
def teach_course(self):
print(f'{self.name}正在教课')
s1 = Student('工藤新一', 19 , 'male')
print(s1.choice_course()) # 工藤新一正在选课
t1 = Teacher() # __init__() missing 3 required positional arguments: 'name', 'age', and 'gender' # 不传参会报错
# 当调用学生类或者老师类的时候 __init__方法会自动执行
# 是因为它们继承person类 也继承了person类的__init__方法
抽象与继承
继承本质应该分为两部分
抽象:将多个类相同的东西抽出去形成一个新的类
继承:将多个类继承刚刚抽取出来的新的类
''' ps: 先抽象后继承!
先写子类 发现 有相同的部分 再总结成父类 这个过程叫抽象
抽象出来了父类 就可以继承了!'''
总结
对象 :数据与功能的结合体
类(子类):多个对象相同数据和功能的结合体
父类:多个类相同数据和功能的结合体
# ps:使用继承节省代码
名字的查找顺序(重中之重)
不继承
class C1:
name = 'alice'
def func(self):
print('from func')
# 1.类
print(C1.name) # 类肯定找的自己的
# 2.对象
obj = C1()
obj.name = 'tifa' # 这不是对象调用类的属性! 这是对象给自己创建独有的属性!
print(obj.__dict__) # 对象的名称空间:{'name': 'tifa'}
print(obj.name) # tifa # 谁近找谁 对象自己有 就找自己
'''对象的名称空间有name 类的名称空间也有name
这时候会用自己的名字 自己有就不会用别人的'''
ps: 对象可以调用类中的名字(obj.func) 或者 在对象内名称空间添加、修改名字(obj.hobby = 'eat') 这样才能修改类的属性 C1.name = 'TIFA'
'''
obj.name 找名字 对象自身>>>类
obj.name = 'alice' 增/改名字 对象自身
'''
总结:
"""
对象查找名字的顺序
1.先从自己的名称空间中查找
2.自己没有再去产生该对象的类中查找
3.如果类中也没有 那么直接报错
对象自身 >>> 产生对象的类
"""
单继承
class F1:
name = 'cloud'
class S1(F1):
name = 'alice'
obj = S1()
obj.__dict__['name'] = 'tifa' # 避免混淆
print(obj.name) # ‘tifa’
'''
对象自身 >>> 产生对象的类 >>> 父类
'''
ps:继承的多也一样,一层一层往上找!
ps:如果所有地方都找不到会报错 报错只报产生对象的类的错!
面试题
class A1:
def func1(self):
print('from A1 func1')
def func2(self):
print('from A1 func2')
self.func1()
class B1(A1):
def func1(self):
print('from B1 func1')
obj = B1()
obj.func2() # 问终端的输出结果是什么?
'''
结果:
from A1 func2
from B1 func1
'''
"""
强调:对象点名字 永远从对象自身开始一步步查找
以后在看到self.名字的时候 一定要搞清楚self指代的是哪个对象
"""
整活
class A1:
def func1(self):
print('from A1 func1')
def func2(self):
print('from A1 func2')
self.func1()
class B1(A1):
def func1(self):
print('from B1 func1')
self.func2() # 又可以递归调用了!!
obj = B1()
obj.func2() # RecursionError: maximum recursion depth exceeded while calling a Python object
# ps:递归和循环哪个效率高???
多继承
class F1:
name = 'miku'
class F2:
name = 'alice'
class F3:
name = 'cloud'
class S1(F1, F2, F3):
pass
obj = S1()
print(obj.name) # miku
'''
对象自身 >>> 产生对象的类 >>> 父类(从左往右)
'''
非菱形继承
特点:没有形成闭环 深度优先
查找顺序:
菱形继承
特点:形成闭环 广度优先
查找顺序:
菱形继承分支断掉的情况
mro()方法
使用mro方法可以看到名字的查找顺序
# 非菱形循环
class A:
# name = 'from A'
pass
class B:
# name = 'from B'
pass
class C:
# name = 'from C'
pass
class D(A):
# name = 'from D'
pass
class E(B):
# name = 'from E'
pass
class F(C):
# name = 'from F'
pass
class S1(D, E, F):
pass
obj = S1()
print(S1.mro())
'''
[<class '__main__.S1'>, <class '__main__.D'>, <class '__main__.A'>, <class '__main__.E'>, <class '__main__.B'>,
<class '__main__.F'>, <class '__main__.C'>, <class 'object'>]'''
'''
S1 -> D -> A -> E -> B -> F -> C
object不考虑
'''
# ps:object不参与任何的操作 当object不存在 但实际每个类都继承了object类
经典类和新式类
object类包含一个类具有的基本条件,规定了类的结构,加载方式,常用函数。
python3中所有类都默认继承object类
"""
经典类:不继承object类或者其子类的类
新式类:继承object类或者其子类的类
在python2中有经典类和新式类
在python3中只有新式类(所有类默认都继承object)
"""
class Student(object):pass
# ps:以后我们在定义类的时候 如果没有其他明确的父类 也可能习惯写object兼容
派生方法(重要)
super() 基本使用
使用派生方法:让子类基于父类中某个方法做扩展:
class Person: # 父类
def __init__(self, name, age, gender):
self.name = name
self.age = age
self.gender = gender
class Student(Person): # 子类想扩展一个hobby属性
def __init__(self, name, age, gender, hobby):
super().__init__(name, age, gender) # super的意思是子类调用父类的方法
self.hobby = hobby
obj = Student('小潮',17 ,'female','eat')
print(obj.__dict__) # {'name': '小潮', 'age': 17, 'gender': 'female', 'hobby': 'eat'}
# 想给student类产生的对象新增一个属性hobby
# 1. 用同名的__init__顶掉父类的双下init
# 2. 新增一个属性hobby
# 3. 使用super 在子类调用父类的方法__init__ 相当于把name、age、gender三个参数传入父类 传谁谁是主人公 此时估计传student对象了 所以会给student对象添加三个属性
# 4. self.hobby = hobby 再给student对象新增一个属性
super完整写法
class Person:
def __init__(self, name, age, gender):
self.name = name
self.age = age
self.gender = gender
class Student(Person):
def __init__(self, name, age, gender, hobby):
super(Student, self).__init__(name, age, gender) # 完整写法
self.hobby = hobby
'''
完整写法
super(当前类名,self).父类的方法名
'''
ps:由于这样写会带来混淆 python3会直接进行省略 只用写super()即可
不用super实现需求
# 例子2
class Person:
def __init__(self, name, age, gender):
self.name = name
self.age = age
self.gender = gender
class Student(Person):
def __init__(self, name, age, gender, hobby):
Person.__init__(self, name, age, gender) # 不用super方法也能实现需求
self.hobby = hobby
obj = Student('小潮',17 ,'female','eat')
print(obj.__dict__) # {'name': '小潮', 'age': 17, 'gender': 'female', 'hobby': 'eat'}
super更多使用
这里的super使得子类调用了父类。如果有多个父类那super调谁?
父类有super,子类的super怎么调?
super改写列表类
class my_list(list):
def append(self, values):
if values == 'key':
print('无法append')
return
super().append(values) # 调用父类的append
obj = my_list()
print(obj) # []
obj.append(1)
obj.append(2)
print(obj) # [1, 2]
obj.append('key') # obj.append(2)
补充例子
# 1.例子
class Person:
def __init__(self, name, age, gender):
self.name = name
self.age = age
self.gender = gender
class Student(Person):
def __init__(self, name, gender, hobby):
super().__init__(name, gender, hobby)
self.hobby = hobby
obj = Student('小潮',17 ,'female')
print(obj.__dict__) # {'name': '小潮', 'age': 17, 'gender': 'female', 'hobby': 'female'}
# 为什么输出结果是这样?