面向对象编程:动静态方法、类的继承、派生类

2022.4.7类相关方法及操作

  • 动态方法与静态方法
  • 面向对象的三大特性之一:继承
  • 继承的名字查找顺序
  • 派生类

一、动态方法与静态方法

1、动态方法

(1)绑定给对象的方法

class Student:
    def run(self):
        print(self)

Student.run(123)  # 类调用绑定给对象的方法:有几个参数就需要传几个参数
obj = Student()
obj.run()  # 对象调用绑定给对象的方法:会自动将对象当作第一个参数传入

(2)绑定给类的方法

class Student:
      @classmethod  # 定义类方法关键字
      def eat(cls):  # 参数一般写cls,约定俗成
        print(cls)
# 类调用绑定给类的方法:会自动将类当做第一个参数传入      
print(Student)  # <class '__main__.Student'>
Student.eat()  # <class '__main__.Student'>)
# 对象调用绑定给类的方法:也不需要传参,会将产生该对象的类自动当做第一个参数传入,其实就是把外层类名传入内层类参数,相当于外层类是内层类的父类,然后通过对象调用内外层类
obj1.eat()  # <class '__main__.Student'>

2、静态方法

class Student:
      @staticmethod  # 定义静态方法关键字
      def speak(a):
          print(a)
静态方法:即普通的函数,定义之后就不会自动传self,但是括号内有几个参数就必须传几个参数
# 类调用静态方法:要自己传值
Student.speak(123)
# 对象调用静态方法:也要自己传值
obj1.speak(321)

二、面向对象的三大特征之一:继承

1、继承的含义

继承,顾名思义,承接别人的东西或者上一代的资源,比如儿子和父亲

面向对象中的继承:即用来描述类与类之间数据的从属关系

ps:类A继承类B,那么类A就可以使用类B中所有的数据(数据、功能...)

2、继承的目的

减少代码冗余,提升开发效率,继承同样支持多继承,即继承多个类

3、继承的基本使用

class A(B):
    ...
# 我们将被继承的类称:父类
# 继承其他类的类称:子类或派生类

class A(B,C,D):
    ...
# python中一个类可以同时继承多个父类

4、继承的本质

个人理解:被继承的类其实就是继承者类和其他类的相同特征抽取出来的,即多个子类,有父类的共同特征

简单来说,就是:

抽象:由下往上抽取相同特征(定义父类时)
继承:由上往下直接白嫖资源(子类继承父类时)

"""
在面向对象编程中 其实类和父类的主要功能都是用来减少代码冗余的

对象:数据与功能的结合体
类:多个对象相同数据和功能的结合体
父类:多个类相同数据和功能的结合体
"""
class Person:  # 定义一个父类,也有子类的相同特征
    def __init__(self, name, age, gender):
        self.name = name
        self.age = age
        self.gender = gender
class Teacher(Person):  # 定义一个子类
    def teach(self):
        print(f'{self.name}老师正在讲课')
class Student(Person):  # 定义一个子类
    def study(self):
        print(f'{self.name}学生正在学习')
stu1 = Student('jason', 18, 'male')  # 生成对象,并向父类传值

三、继承的名字查找顺序

1、不继承的情况下

先从对象自己的名称空间中查找,没有则去类中的名称空间查找

对象>>>类

注意:使用对象修改或添加值只能修改对象本身,不能修改类中值

class A:
    a = 'from A'
obj = A()  # 生成对象obj,此时obj相当于一个空字典
a = 'from obj'
obj.append(a)  # 对象中添加数据
print(obj.a)  # 'from obj'
obj.pop(a)  # 删除对象数据a
print(obj.a)  # 'from A'

2、单继承的情况下

先从对象自己的名称空间查找,没有再从类中查找,没有的话,若有父类,再从父类中查找

对象>>>类>>>父类

class A:
      def f1(self):
          print('from A.f1')
      def f2(self):
          print('from A.f2')
          self.f1()  # 相当于obj.f1(),看到self.东西 一定要问自己self是谁
class MyClass(A):
	def f1(self):
	print('from MyClass.f1')
obj = MyClass()
obj.f2()  # 执行的方法分别是A里面的f2和MyClass里面的f1

3、多继承的情况下

多继承的情况下python2与python3有所不同:

"""
	在python2中存在经典类与新式类
	在python3中只有新式类
	区分的关键在于是否继承了一个默认的object类
		新式类:直接或者间接继承了object或者其子类的类
		经典类:不继承任何的类
"""
class A:  # python2环境
    pass
class B:  # python3环境
    pass
print A.__bases__  # 空的
print(B.__bases__)  # (<class 'object'>,)
因此,python3中时默认继承了一个object类,如果要写的时候同时兼容python2和python3,可以一律加上(object)
    class MyClass(object):
        pass
注意:研究菱形和非菱形问题object不参与图形构建,下面会介绍菱形和非菱形继承

(1)继承多个类情况下

,名字查找顺序是从左往右的,即:

class A(B,C,D):  # 默认先找类B,然后是A,最后是D
    ...

那么什么是非菱形继承和菱形继承呢

(2)非菱形继承

非菱形继承的情况下
	父类中名字的查找顺序就是按照继承时从左往右依次查找
	如果多个父类还有分类 那么遵循"深度优先"
class C:
    pass
class E:
    pass    
class B(C):
    pass   
class D(E):
    pass
class A(B,D):
    pass
此情况查找顺序如下图

LScfi9.png

(3)菱形继承

菱形继承的情况下
  	父类中名字的查找顺序就是按照继承时从左往右依次查找
   	如果多个父类还有分类 那么遵循"广度优先"
class F:
    pass
class C(F):
    pass
class E(F):
    pass    
class B(C):
    pass   
class D(E):
    pass
class A(B,D):
    pass
此情况查找顺序如下图

LS21nf.md.png

永恒真理:名字查找顺序永远是从当前对象自身开始查找

四、派生类

概念:如果自己写的子类需要使用父类的方法 并且还需要基于该方法做扩展
这样的子类我们称之为派生类(本质还是子类)
那么可以使用super关键字来实现

首先定义父类和子类
class Person:
    def __init__(self,name,age,gender):
        self.name = name
        self.age = age
        self.gender = gender
子类:teacher
class Teacher(Person):
    def __init__(self,name,age,gender,level,salary):
        # 用了Person类里面的__init__方法之后,如何创建自己的特有属性呢
        两种写法:
      1.super(Teacher,self).__init__(name,age,gender)  # 子类调用父类的方法 完整语法
      2.super().__init__(name,age,gender)  # 子类调用父类的方法 精简语法
        # 自己还要添加一个额外的东西
        self.level = level
        self.salary = salary
子类:student
class Student(Person):
    def __init__(self,name,age,gender,stu_id,class_id):
        # 用了Person类里面的__init__方法之后
        super().__init__(name, age, gender)
        # 自己还要添加一个额外的东西
        self.stu_id = stu_id
        self.class_id = class_id
t1 = Teacher('jason',18,'male','满级',3.1)  # 使用父类方法
s1 = Student('kevin',28,'female',1302010201,2)
print(t1.__dict__)
print(s1.__dict__)

五、函数和方法的区别

from types import MethodType,FunctionType
class Foo:
    def test(self):
        print('绑定给对象的方法,对象来调用是方法,类来调用是函数')

    @classmethod
    def test2(cls):
        print('绑定给类的方法,类来调用是方法,对象来调用是方法')

    @staticmethod
    def test3():
        print('静态方法,类来调用是函数,对象来调用是函数')


# 类来调用静态方法:静态方法就是函数
print(isinstance(Foo.test3,MethodType))  # False
print(isinstance(Foo.test3,FunctionType)) # True

# 类调用类的绑定方法
print(isinstance(Foo.test2,MethodType))  # True
print(isinstance(Foo.test2,FunctionType))  # False

# 类调用对象的绑定方法
print(isinstance(Foo.test,MethodType))  # False
print(isinstance(Foo.test,FunctionType))  # True

#对象调用对象的绑定方法
foo=Foo()
print(isinstance(foo.test,MethodType))  # True
print(isinstance(foo.test,FunctionType))  # False

# 对象调用类的绑定方法
print(isinstance(foo.test2,MethodType))  # True
print(isinstance(foo.test2,FunctionType))  # False

# 对象调用静态方法
print(isinstance(foo.test3,MethodType))  # False
print(isinstance(foo.test3,FunctionType))  # True

# 函数,有几个值就要传几个值
# 方法,可以自动传值,对象的绑定方法,类的绑定方法

派生功能前瞻

class MyClass(list):  # 可直接继承内置方法
    def append(self,args):  # 自己定义一个append方法
        if args == 123:  # 判断条件后执行后面代码
            print('数字123不能追加')
            return
        super(MyClass, self).append(args)  # 筛选条件后添加传进来的args
obj1 = MyClass()  # 生成对象obj
obj1.append(333)  # 走super方法,添加数据到obj
obj1.append(222)  # 走super方法,添加数据到obj
obj1.append(123)  # 走if判断,直接退出
print(obj1)  # {333,222}
posted @ 2022-04-07 22:27  马氵寿  阅读(129)  评论(0)    收藏  举报