动态方法与静态方法,面向对象的三大特征

动态与静态方法

普通方法

  只有实例化对象之后才可以使用的方法,该方法的第一个形参接收的一定是对象本身!如果使用类名.函数
名调用普通方法会报错。
  class Cat:
    '''定义了一个Cat类'''
    # 方法
    def eat(self):
        print("猫在吃鱼....")

静态方法

  静态方法是类中的函数,不需要实例

  主要用来存放逻辑性的代码,与类本身没有交互,即在静态方法中不会涉及到类内的方法和属性若想调用类
内的方法和属性则可通过(__class__.类属性)

  形式:添加@staticmethod装饰器

  调用:类名.类方法名(或者对象名.类方法名)

  参数:静态方法可以有参数也可以无参数

  class Cat:
    '''定义了一个Cat类'''
    n = 1
    # 方法
    @staticmethod
    def eat():
        print(__class__.n)
        print("猫在吃鱼....")
  Cat.eat()
  # 创建一个对象
  tom = Cat()
  tom.eat()

动态方法

绑定给对象的方法

  class Cat:
    '''定义了一个Cat类'''
    # 方法
    def drink(self):
        print("猫正在喝kele.....")
  tom = Cat()
  tom.drink() 对象调用绑定给对象的方法:会自动将对象当做第一个参数传入
  
  '''当类调用绑定给对象的方法时需要传入参数,如果没参数会报错。'''
  Cat.drink() 
  ''' TypeError: Cat.drink() missing 1 required positional argument: 'self' '''
  Cat.drink(1)
  猫正在喝kele.....

绑定给类的方法(类方法)

  无需实例化,可以通过类直接调用的方法,但是方法的第一个参数接收的一定是类本身

  (1)在方法上面添加@classmethod

  (2)方法的参数为 cls 也可以是其他名称,但是一般默认为cls

  (3)cls 指向 类对象

  (5)应用场景:当一个方法中只涉及到静态属性的时候可以使用类方法(类方法用来修改类属性)。

  (5)使用 可以是 对象名.类方法名 或者是 类名.类方法名

  class Cat:
    '''定义了一个Cat类'''
    name = '小猫咪'
    @classmethod
    def drink(cls):
        print(cls.name)
        print("猫正在喝kele.....")
  类调用绑定给类的方法:会自动将类当做第一个参数传入
  Cat.drink()
  小猫咪
  猫正在喝kele.....
  # 创建一个对象
  tom = Cat()
  对象调用绑定给类的方法:也不需要传参 会讲产生该对象的类自动当做第一个参数传入
  tom.drink()
  小猫咪
  猫正在喝kele.....

面向对象的三大特征

封装

  封装的概念:就是将复杂的信息、流程给包起来,内部处理,让使用者只需要通过简单的操作步骤,就能实
现。

继承

  基本概念:在面向对象的世界中,从父类继承,就可以直接拥有父类的属性和方法,这样可以减少代码、多
复用。子类可以定义自己的属性和方法

  基本使用:
    """
		class A(B):  
			pass
	我们将被继承的类称为:父类或者基类     B
        继承别人的类称为:子类或者派生类	A
    """

  如果类定义时,没有继承别的类,等同于继承自object。在Python3中,object类是所有对象的根基
类
    class A:
  	pass
    # 等价于
    class A(object):
    	pass
  
   在python中一个类可以同时继承多个父类
  	class A(B,C,D):
            pass

继承中__init__()方法

  __init__方法的作用是初始化类中的属性。当你创建实例的时候,类中的__init__方法会自动执行。不
需要你显式的调用。
'''
  class A(object):
    def __init__(self):
        print('._init_方法已执行')
  example1 = A()
  ._init_方法已执行
'''
   注意:__init__()方法中第一个形参必须是 self。 形参self做作用很大。每个与类相关联的方法
调用都自动传递实参self,它是一个指向实例本身的引用,让实例能够访问类中的属性和方法。
子类没有定义自己的初始化函数,父类的初始化函数会被默认调用
  class parent():
      def __init__(self,race,country):
          self.race = race
          self.country = country
      def information(self):
          print('我来自{},是{}人'.format(self.country,self.race))
      def test(self):
          print('父类的方法')
          
  class son(parent):
      pass

  example1 = son('中国','浙江')
  example1.information()
  我来自浙江,是中国人
   
  '''
    tip1:此时需要注意,当子类实例化时,由于子类没有初始化,此时父类的初始化函数就会默认被调用,
且必须传入父类的参数。
  如果不传入父类的参数就会报错
  '''
子类定义了自己的初始化函数,而在子类中没有显式调用父类的初始化函数,则父类的属性不会被初始化
  class parent():
      def __init__(self,race,country):
          self.race = race
          self.country = country
      def information(self):
          print('我来自{},是{}人'.format(self.country,self.race))
      def test(self):
          print('父类的方法')
          
  class son(parent):
      def __init__(self):
          pass
   
  example1 = son()
  example1.test()
  父类的方法
  '''tip1:此时用子类创建实例的时候,就按照子类的初始化函数传入实参。如果还按照父类的就会报错'''
  '''tip2: 父类的属性没有被初始化,无法通过子类创建的实例来直接访问父类中的属性'''
  '''tip3: 此时还是可以访问父类中的方法的'''
如果子类定义了自己的初始化函数,显式调用父类,子类和父类的属性都会被初始化
  class parent():
      def __init__(self,race,country):
          self.race = race
          self.country = country
      def information(self):
          print('我来自{},是{}人'.format(self.country,self.race))
      def test(self):
          print('父类的方法')
          
 class son(parent):
    def __init__(self, race, country,name):
        super().__init__(race,country)
        # super(son, self).__init__(race,country)
        pass
   
  example1 = son('中国','浙江',111)
  example1.information()
  我来自浙江,是中国人
   
  '''tip1:此时子类中初始化函数中参数必须包括父类中的参数,也就是父类初始化函数的形参是子类初始化函数中形参的子集,否则会报错'''
   
  '''即super().__init__(*arg1)中的*arg1  必须是 __init__(self,*args2) 的*args2的子集'''
  ''' *arg1 ∈ *args2 '''
三种情况的总结
1、子类中若有__init__()方法,则创建实例时传入的参数,须与__init__()方法中形参数量一致(self不算在内)

2、这三种情况,父类中的方法都是可以调用的

3、使用子类创建实例的时候,父类的初始化方法必须被子类显式调用才会执行。而子类的初始化方法会自动执行

单继承

  名字的查找顺序是
  	先从对象自己的名称空间中查找 没有则去产生对象的类中查找
    如果还没有并且类有父类则去父类中查找 以此往复下去!!!
    	对象	>>>	类	>>> 父类

   经典案例
  	class A:
            def f1(self):
                print('from A.f1')
            def f2(self):
                print('from A.f2')
                self.f1()  '''以后看到self点东西 一定要问自己self是谁'''
        class MyClass(A):
            def f1(self):
                print('from MyClass.f1')
        obj = MyClass()
        obj.f2()

        执行结果:
        from A.f2
        from MyClass.f1

多继承(了解)

  #父类
  class Person:  # 默认直接继承自obj
      def eat(self):
          print("人吃饭!")
      def play(self):
          print("happy!")
   
  class Man:   # 默认直接继承自obj
      def eat(self):
          print("男人吃饭!")
      def drink(self):
          print('男人抽烟')
   
  # 子类
  class Children(Person,Man):
      def eat(self):
          print("孩子吃饭!")
      pass
   
  #创建对象
  ch=Children()
  ch.eat()
  ch.drink()
  # (<class '__main__.Children'>, <class '__main__.Person'>, <class '__main__.Man'>, <class 'object'>)
  print(Children.__mro__)

  '''
      如果方法同名,首先优先调用自己的,如果自己没有,那么就按照继承关系,从左至右一个个寻找;
      继承关系  Children >  Person > Man > object
  '''

  '''
        非菱形继承的情况下
  	父类中名字的查找顺序就是按照继承时从左往右依次查找
   	如果多个父类还有分类 那么遵循"深度优先"
  		ADBECF
        菱形继承的情况下
  	父类中名字的查找顺序就是按照继承时从左往右依次查找
   	如果多个父类还有分类 那么遵循"广度优先"
  		ADBECFM
  '''

#############################################
'''名字的查找顺序永远都是 先从当前对象自身开始查找'''
#############################################

方法重写覆盖

  基类(被继承的类)的成员都会被派生类(继承的新类)继承,当基类中的某个方法不完全适用于派生类时,就
需要在派生类中重写父类的这个方法。

  基类中定义的harvest()方法,无论派生类是什么水果都显示"水果…",如果想要针对不同水果给出不同的
提示,可以在派生类中重写harvest()方法。例如,在创建派生类Orange()时,重写harvest()方法如下:

  class Fruit:
      color = '绿色'

      def harvest(self, color):
          print(f"水果是:{color}的!")
          print("水果已经收获...")
          print(f"水果原来是{Fruit.color}的!")


  class Apple(Fruit):
      color = "红色"

      def __init__(self):
          print("我是苹果")


  class Orange(Fruit):
      color = "橙色"

      def __init__(self):
          print("\n我是橘子")

      def harvest(self, color):  # 重写harvest 
          print(f"橘子是:{color}的!")
          print("橘子已经收获...")
          print(f"橘子原来是{Fruit.color}的!")


  apple = Apple()  # 实例化Apple()类
  apple.harvest(apple.color)  # 在Apple()中调用harvest方法,并将Apple()的color变量传入
  orange = Orange()
  orange.harvest(orange.color)  # 在Orange()中调用harvest方法,并将Orange()的color变量传入

  执行结果:
  	我是苹果
  	水果是:红色的!
  	水果已经收获...
  	水果原来是绿色的!
  	
  	我是橘子
  	橘子是:橙色的!
  	橘子已经收获...
  	橘子原来是绿色的!

'''
  如子类中和父类同时存在这个方法名称,将只会执行子类中的这个方法,不会调用父类的同名方法(包括__init__())
'''

派生类

   派生就是子类在继承父类的基础上衍生出新的属性。子类中独有的,父类中没有的;或子类定义与父类
重名的东西。子类也叫派生类。
  class Animal:
      def run(self):
          print('小动物喜欢成天跑个不停')
   
   
  class Cat(Animal):
      def eat(self):
          print('猫喜欢吃老鼠')
   
   
  tom = Cat()
   
  tom.run()
  # 多出来的行为
  tom.eat()
   
  # 多出来的属性,对象的属性是可以随时添加的,派生
  tom.color = '蓝色'
  print(tom.color)

super关键字

  我们举一个简单例子,我们父类是Human,有两个属性,分别是姓名和性别;然后定义一个子类Student。
  class Human:
      def __init__(self, name, sex):
          self.name = name
          self.sex = sex
          print('parent')
  class Student(Human):
      pass
  stu_1 = Student('lisi', 'male')
  print(stu_1.name)

  这里Student没有构造函数,所以会去父类中寻找构造函数。这时候我们需要在子类中加入构造函数,并需
要name,sex,score三个属性,那我们如果直接写就应该是下面这种代码。
  class Human:
      def __init__(self, name, sex):
          self.name = name
          self.sex = sex
          print('parent')
  class Student(Human):
      def __init__(self, name, sex, score):
          self.name = name
          self.sex = sex
          self.score = score
          print('child')
  stu_1 = Student('lisi', 'male', 97)
  print(stu_1.score)

  你会发现父类和子类都有相同的两行代码。
  self.name = name
  self.sex = sex

  这显然和我们优雅的Python格格不入,所以super函数来了,我们直接看代码。
  class Human:
      def __init__(self, name, sex):
          self.name = name
          self.sex = sex
          print('parent')
  class Student(Human):
      def __init__(self, name, sex, score):
          super().__init__(name, sex)
          self.score = score
          print('child')
  stu_1 = Student('lisi', 'male', 97)
  print(stu_1.score)
  通过代码我们可以看出,super函数常常用于子类的构造函数中,用于调用父类(超类)的构造函数,并且不
会显式引用基类。
  此时子类中初始化函数中参数必须包括父类中的参数,也就是父类初始化函数的形参是子类初始化函数中
形参的子集,否则会报错

不仅仅是用于构造函数

  super函数虽常用于构造函数,但是父类的其他函数一样也是可以用super函数的。
  class A:
      def add(self, x):
          y = x + 1
          print(y)
  class B(A):
      def add(self, x):
          super().add(x)
  b = B()
  b.add(2)
  # 3
  之所以不常用,我认为是既然继承了父类,那子类就可以直接调用父类的方法,这样做只是多此一举。
  class A:
      def add(self, x):
          y = x + 1
          print(y)
  class B(A):
      pass
  b = B()
  b.add(2)
  # 3

多重继承可能就不一样了

  根据上面的案例,我们可以看出super函数是直接调用基类的构造函数,但是多重继承不一样,他是调用继
承顺序的下一个类,而不是父类。
  class Base:
      def __init__(self):
          print("enter Base")
          print("leave Base")
  class A(Base):
      def __init__(self):
          print("enter A")
          super().__init__()
          print("leave A")
  class B(Base):
      def __init__(self):
          print("enter B")
          super().__init__()
          print("leave B")
  class C(A, B):
      def __init__(self):
          print("enter C")
          super().__init__()
          print("leave C")
  C()

  enter C
  enter A
  enter B
  enter Base
  leave Base
  leave B
  leave A
  leave C

super函数和直接调用父类方法的区别

  在单继承时,我们看到super和直接调用父类方法得到的结果是一样的,只是不会显式引用基类。但多重继
承就不一样了,我把上面的代码进行了修改,我相信你能看懂区别。
  class Base:
      def __init__(self):
          print("enter Base")
          print("leave Base")
  class A(Base):
      def __init__(self):
          print("enter A")
          Base.__init__(self)
          print("leave A")
  class B(Base):
      def __init__(self):
          print("enter B")
          Base.__init__(self)
          print("leave B")
  class C(A, B):
      def __init__(self):
          print("enter C")
          A.__init__(self)
          B.__init__(self)
          print("leave C")
  C()

  enter C
  enter A
  enter Base
  leave Base
  leave A
  enter B
  enter Base
  leave Base
  leave B
  leave C
posted @ 2022-04-07 23:05  春游去动物园  阅读(248)  评论(0编辑  收藏  举报