面向对象深入

动静态方法

总的来说就是在类中定义的函数有多种特性。

动态方法

类中的方法,会做一些自动的操作,比如自动把对象作为第一个参数传进去。

类中直接定义函数

类中直接定义函数 这个函数默认绑定给对象 类调用有几个参数传几个 对象调用第一个参数就是对象自身

# 绑定给对象的方法
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: 先抽象后继承!
	先写子类 发现 有相同的部分 再总结成父类 这个过程叫抽象 
	抽象出来了父类 就可以继承了!'''

image
image

总结

对象 :数据与功能的结合体
类(子类):多个对象相同数据和功能的结合体
父类:多个类相同数据和功能的结合体
# 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指代的是哪个对象
"""

image

整活

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
 '''
      对象自身   >>>   产生对象的类    >>>    父类(从左往右)
 '''

非菱形继承

特点:没有形成闭环 深度优先
查找顺序:
image

菱形继承

特点:形成闭环 广度优先
查找顺序:
image

菱形继承分支断掉的情况

image

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'}

# 为什么输出结果是这样?
posted @ 2022-11-03 20:01  passion2021  阅读(61)  评论(0编辑  收藏  举报