Python学习笔记--面向对象--进阶

1.一切皆对象,什么是一切皆对象?

  • python中,创建一个学生类,也就是创建了一个类型叫学生类。
  • class Student:
        def __init__(self, x, y, z):
            self.name = x
            self.age = y
            self.gender = z
    
        def choose(self):
            pass
    
    
    obj1 = Student("liqi", 18, "male")
    print(type(obj1))   # <class '__main__.Student'>
    print(Student)  # <class '__main__.Student'>
    # 上面这两个是一样的。

     

2.用样的,list也是一个类?

  • 对的。
  • class Student:
        def __init__(self, x, y, z):
            self.name = x
            self.age = y
            self.gender = z
    
        def choose(self):
            pass
    
    
    obj1 = Student("liqi", 18, "male")
    print(type(obj1))   # <class '__main__.Student'>
    print(Student)  # <class '__main__.Student'>
    # 上面这两个是一样的。
    
    print(list) # <class 'list'>
    l1 = list([1,2,3])
    print(l1,type(l1))  # [1, 2, 3] <class 'list'>
    l1.append(4)    # 增加4
    print(l1)
    
    list.append(l1,777) # 同样增加了,用类的方法,把obj传进去
    print(l1)   # [1, 2, 3, 4, 777]
    View Code

     

 3.property是什么?

  • 是一个装饰器,用来把方法变为属性。
  • 比如,BMI这个值,是计算出来的,以函数的形式存在,但我们想把这个变为属性,就用到property
  • 代码示例:
    •   
      class Student:
          def __init__(self, name, weight, height):
              self.name = name
              self.weight = weight
              self.height = height
      
          @property
          def bmi(self):  # 计算BMI,公式为体重/身高的平方
              # BMI = 75kg / (1.75m^2)
              return self.weight / (self.height ** 2)
      
      
      obj1 = Student("LIQI", 65, 1.6)
      print(obj1.bmi)
      # 但是bmi给人感觉是个数据,是个属性,而这里是个方法。
      # 如何把这个方法变为属性呢?用到@property 把方法变为他的属性

       

 4.那被property装饰后的隐藏name属性,在外部如何修改呢?

  • 利用@name.setter
  • 示例代码:
    • class Student:
          def __init__(self, name):
              self.__name = name  # 把name保护起来
      
          @property
          def name(self):
              return self.__name
      
          @name.setter    # 像正常属性一样可以赋值
          def name(self, new_name):
              self.__name = new_name
      
          @name.deleter   # 不可以删除
          def name(self):
              print("不允许删除~!")
      
      
      obj1 = Student("liqi")  # 正常执行
      print(obj1.name)
      
      obj1.name = "tutu"  # 修改
      print(obj1.name)
      
      del obj1.name   # 删除

       

  • 示例代码2(这样也可以)
    •   
      class Student:
          def __init__(self, name):
              self.__name = name  # 把name保护起来
      
          def get_name(self):
              return self.__name
      
          def set_name(self, new_name):
              self.__name = new_name
      
          def del_name(self):
              print("11不允许删除~!")
      
          name = property(get_name, set_name, del_name)
      
      
      obj1 = Student("liqi")  # 正常执行
      print(obj1.name)
      
      obj1.name = "tutu"  # 修改
      print(obj1.name)
      
      del obj1.name  # 删除

       

 5.classmethod是什么?

  • 被classmethod修饰过的函数,是绑定给类的方法。
  • 正常的函数,是默认绑定给对象的方法。
  • 绑定给谁就应该由谁来调用。
  • 示例代码:
    • # 绑定方法
      #   特点:绑定给谁就应该由谁来调用,谁来调用就会将自己当做第一个参数传入
      
      # 非绑定方法
      #   特点:不与类和对象绑定,意味着谁都可以来调用,但无论谁来调用就是一个普通函数,没有自动传参的效果
      class People:
          def __init__(self, name):
              self.name = name
      
          # 但凡在类中定义一个函数,默认就是绑定给对象的,应该由对象来调用
          # 调用时,会将对象作为第一个参数自动传入
          def tell(self):
              print(self.name)
      
          # 类中定义的函数被classmethod装饰过,就绑定给类,应该由类来调用
          # 调用时,会将类本身第一个参数自动传入
          @classmethod
          def func(cls):
              print(cls)
      
      
      People.func()  # <class '__main__.People'>

       

 6.可以不绑定给任何类,也不绑定给对象吗?

  • 可以。用staticmethod来实现。

7.staticmethod是什么?

  • 不绑定类,也不绑定给对象,就是单纯的一个函数。
  • 示例代码:
    • # 绑定方法
      #   特点:绑定给谁就应该由谁来调用,谁来调用就会将自己当做第一个参数传入
      
      # 非绑定方法
      #   特点:不与类和对象绑定,意味着谁都可以来调用,但无论谁来调用就是一个普通函数,没有自动传参的效果
      class People:
          def __init__(self, name):
              self.name = name
      
          # 但凡在类中定义一个函数,默认就是绑定给对象的,应该由对象来调用
          # 调用时,会将对象作为第一个参数自动传入
          def tell(self):
              print(self.name)
      
          # 类中定义的函数被classmethod装饰过,就绑定给类,应该由类来调用
          # 调用时,会将类本身第一个参数自动传入
          @classmethod
          def func(cls):
              print(cls)
      
          # 类中定义的函数被staticmethod装饰过,不绑定给任何,谁都可以来调用
          # 调用时,当做普通函数来使用
          @staticmethod
          def demo(x,y):
              print(x,y)
      
      
      # People.func()  # <class '__main__.People'>
      People.demo(4,5)
      obj = People("tom")
      obj.demo(3,2)

       

8.什么时候用对象的方法,什么时候用类的方法?什么时候用静态方法(普通函数)?

  • 他们的区别,也就是应用的不同。
  • 示例:
    • 场景1,类的参数,从配置文件导的时候。
    • 场景2,对象,类都可以的调用,来生成uuid,不需要传参数。
  • 示例代码:
    • import setting
      
      
      class People:
          def __init__(self, name, age, gender):
              self.name = name
              self.age = age
              self.gender = gender
      
          def tell_info(self):
              print(f'名字:{self.name},年龄:{self.gender},性别:{self.gender}')
      
          @classmethod
          def from_conf(cls):
              print("类的方法==>", cls)
              return cls(setting.NAME, setting.AGE, setting.GENDER)
      
          @staticmethod
          def creat_id():
              import uuid
      
              return uuid.uuid1()
      
      
      p1 = People("tutu", 18, "male")
      
      # p2 = People.from_conf()
      # print(p2.__dict__)  # {'name': 'liqi', 'age': 18, 'gender': 'female'}
      
      print(p1.creat_id())  # 对象可以来调用
      print(People.creat_id())  # 类也可以来调用

       

 9.继承是解决什么问题的?

  • 继承是解决,类与类之间的冗余问题的。
  • (类是解决对象与对象之间的冗余的问题)

 10.什么是继承?

  • 继承是创建一种新类的方式。
  • 新建的类,称之为子类。
  • 被继承的类,称之为父类or基类or超类。

 11.python支持多继承吗?

  • 支持
  • 示例代码:(用__bases__查看集成的父类项)
    • class People1:
          pass
      
      class People2:
          pass
      
      class obj1(People1):
          pass
      
      class obj2(People1,People2):
          pass
      
      print(obj1.__bases__)   # (<class '__main__.People1'>,)
      print(obj2.__bases__)   # (<class '__main__.People1'>, <class '__main__.People2'>)

       

12.什么是新式类,经典类?(了解)

  • python3不区分,都是是继承object,也就是都是新式类。
  • python2区分,凡是继承object的都是新式类,其他都是经典类。

 

 13.python支持多继承,好处是?

  • 先说结论:提供代码的利用率。
  • 具体:
    • 继承是为了解决类与类的冗余问题。
    • 一个儿子,可以继承1个父类时候,这个父类有的功能,子类就不用写了。
    • 一个儿子,可以继承N个父类时候,这些父类的N个功能,子类都不用写了。
  • 多继承,父类的多个功能,都能复用,不用写重复的代码。
  • (扩展)java只支持单继承。

16.python多继承的坏处?

  • 找东西会乱。
  • 会有强耦合。
  • 所以慎用

 

17.一个选课系统,有老师,管理员,学生类,需要写一个父类吗?

  • 慎用。
  • 当老师类,管理员类,学生类都有大量公共的部分的时候,可以用。
  • 当他们只有少量的公共,不建议写父类。

18.有继承关系的查找顺序是?

  • 对象-->类-->父类
  • 示例代码:
    •   
      class Bar:
          z = 999
      
      
      class People(Bar):
          y = 777
      
          def __init__(self, x):
              self.x = x
      
      
      obj = People(123)
      print(obj.__dict__)
      print(obj.x)    # 去对象找
      print(obj.y)  # 去类找到y
      print(obj.z)  # 去父类找到z

       

19.派生什么意思?

  • 本指江河的源头产生出支流,引申为从一个主要事物的发展中分化出来。——百度百科
  • 类中的派生,就是继承父类的基础上,又添加了自己的新的功能。

 20.先抽象后继承什么意思?

  • 对象与对象,抽出公共部分形成类;类与类,抽出公共部分形成父类。
  • 继承是先继承父类,再继承类。

21.选课系统中学生的数据有哪些?如何定义这个学生类?

  • 面向对象的特点是扩展性强。
  • 所以,没必要要求自己上来就设计的十全十美。
  • 可以边设计边想。
  • 比如,数据有:学生学号,专业,班级,校区。功能有:选课功能
  • 后续再想。

 

 22.一个基本的类怎么构成?

  • 对象个性化的数据,放到init
  • 对象通用的数据,放到类里。
  • 对象都应该具备的功能,绑定给对象。
  • 示例代码:
    •   
      class Student:
          school = "北京大学"  # 类的具有的数据
      
          def __init__(self, name, age):  # 对象自己特有的数据
              self.name = name
              self.name = age
      
          def choose(self):  # 对象应该具备的功能
              print(f"{self.name}选择单片机课程")

       

 23.在子类派生的新方法,重用父类的功能

  • 方式1:指名道姓的引用某一个类的函数,与继承无关

  • 代码:
    • class People:
          def __init__(self, name, age, gender):
              self.name = name
              self.age = age
              self.gender = gender
      
      
      class Student(People):
          # 当子类延续使用父类的属性,这里init可以不用写
          def choose(self):
              print(self.name)
      
      
      class Teacher:  # Teacher(People) 
          def __init__(self, name, age, gender, level):   # 形参
              People.__init__(self, name, age, gender)  # 指名道姓的使用 实参
              self.level = level
      
          def func(self):
              print(self.name,self.age,self.gender,self.level)
      
      
      stu1 = Student("liqi", 18, "")
      stu1.choose()
      
      tea1 = Teacher("egon",28,"male",10)
      tea1.func()

       

  • 方式2:利用super,依赖继承关系。

  • super()返回一个特殊对象,该对象会参考自己当前类的mro列表,去当前的父类中找属性。
  • 使用的时候,是调用这个super特殊函数的来实现,所以要加括号。
  • 代码:
    • class People:
          def __init__(self, name, age, gender):
              self.name = name
              self.age = age
              self.gender = gender
      # super返回一个特殊的对象,该对象会参考当前的mro列表,去当前的父类查找属性
      # 需要调用这个super方法,是个函数,需要加()
      
      class Teacher(People):
          def __init__(self, name, age, gender, level):
              # People.__init__(self, name, age, gender)  # 指名道姓的使用 实参
              super().__init__(name, age, gender)  # 因为super也是对象,所以不用self,记得加括号
              self.level = level
      
          def func(self):
              print(self.name, self.age, self.gender, self.level)
      
      tea1 = Teacher("egon", 28, "male", 10)
      tea1.func()

       

 

24.python如何确定类的查找顺序?

  • MRO算法
  • 可以通过打印类的mro来查看。
  • 示例:
    • class C:
          x = 333
      
      class B(C):
          x = 222
      
      class A(B):
          x = 111
      
      print(A.mro())  
      # [<class '__main__.A'>, <class '__main__.B'>, <class '__main__.C'>, <class 'object'>]

       

        

 25.super是基于发起属性查找的那个类开始查找?

  • 是的

  • 代码:
    • class A:
          def test(self):
              super().test()
      
      class B:
          def test(self):
              print("bbbb")
      
      class C(A,B):   # mro [C,A,B,object]
          pass
      
      obj = C()
      obj.test()

       

26.继承表达的是is-a关系? 

  • 对的。
  • 比如,老师是人,学生是人,他们就都可以继承一个人的属性。

27.为什么有的人写代码清晰?

  • 代码能体现人的逻辑,有这个清晰的划分,就是好的代码。质量过关的代码。
  • 比如:善用继承关系,又慎用继承关系。
  • 比如:多继承关系,有些乱。相当于一个人,有多个父亲,有多个母亲,就比较乱。不怎么顺应人类的理解。

 

28.如何一定要用多继承呢?

  • 用一个主类,作为基准。
    • 其他的类,当做修饰。命名上加上尾部Mixin来区分。
    • 基本类,放在最右边。
    • 修饰类,只放功能。
    • 像__init__类,就不放入修饰类,放入基本类。
    • 保证如果没有修饰类,基本类也可以实例化运行。
  • 类比就是:我是小明,我是人类这个基本类,混入了音乐类(爱好音乐),混入了体育类(爱好体育),混入了读书类(爱好读书)
  • 例如socketserver模块

     

     

     

29.组合是什么?

  • 给对象,增加属性的。
    •  让1个对象--->超级大对象的集合。
  • 比如,表达小明在班级里,小明有一个书包。这种,位置,所在;持有,所有的这种。用组合。
  • 示例代码:
    • class People:
          def __init__(self, name, age):
              self.name = name
              self.age = age
      
      
      class Student(People):
          def __init__(self, name, age, student_nums):  # 外部传入【名字,年龄,学号】
              super().__init__(name, age)  # 调用super()函数的init方法,传入【名字,年龄】表达继承
              self.id = student_nums
      
          def go_to_class(self):
              print(f"{self.name}正在班级上课...")
      
      
      class Bag:
          def __init__(self, size, color, style):  # 外部传入【大小,颜色,样式】
              self.size = size
              self.color = color
              self.style = style
      
          def let_me_see(self):
              print(f"这个书包的大小是{self.size},颜色是{self.color},样式是{self.style}")
      
      
      class ClassSchool:
          def __init__(self, class_name):  # 外部传入【班级名称】
              self.class_name = class_name
      
          def let_me_see(self):
              print(f"班级是{self.class_name}")
      
      # 实例化对象
      lihua = Student("李华", 18, "003")
      hello_kitty = Bag("小的", "粉色", "hellokitty的")
      class_school_1 = ClassSchool("3年1班")
      
      # 组合
      # 需要表示: 李华有书包,李华在班级里
      lihua.bag = hello_kitty
      lihua.class_schol = class_school_1
      
      # 查看李华的信息
      print(lihua.bag.size)   # 查看书包的大小
      lihua.bag.let_me_see()  # 查看书包的所有信息
      
      print(lihua.class_schol.class_name) # 查看班级的名字
      lihua.class_schol.let_me_see()  # 查看班级的信息

       

    • 结果:
    •  

       

30.什么时候用单继承,什么是时候用多继承,什么时候用组合?

  • 定性的,用单继承。

    • 比如,李华这个对象,李华是学生类,学生是人类。
  • 修饰的,用多继承。

    • 比如,李华这个对象,李华是学生类,李华混入音乐类(爱好,表修饰),李华混入读书类(爱好),李华混入体育类(爱好)
  • 位置的,所有的,用组合。

    • 比如,李华这个对象,李华是学生类;hellokitty书包是对象,hellokitty书包是物品类;李华有hellokitty书包,就是表达所有。
    • 比如,李华这个对象,李华是学生类;3年1班是对象,3年1班是班级类;李华在班级里,就是表达位置。

 31.什么是多态?

  • 同一种事物,有多重形态。
  • 比如,水。有气态,液态,固态。
  • 比如,动物。有猪,人,狗,兔子,等等。
  • 代码实现的时候,就是一个父类,有多个子类。
  • 比如:
    • class Animal:
          pass
      
      class Big(Animal):
          pass
      
      class People(Animal):
          pass
      
      class Dog(Animal):
          pass

       

32.什么是鸭子类型?

  • 维基百科:

  • 鸭子类型(duck typing)在程序设计中是动态类型的一种风格。在这种风格中,一个对象有效的语义,不是由继承自特定的类或实现特定的接口,而是由"当前方法和属性的集合"决定。——《维基百科》
  • 通俗一点是:

  • 当看到一只大白鹅走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只大白鹅就可以被称为鸭子。
  • 我的理解:

    • 当我们设计一种架构的时候,可以用强制去约束各个类之间的关系。也可以用“君子协商”来约定(弱约束)。
    • 而Python是一种动态语言,不像Java和C++这种强类型语言,Python里实际上没有严格的类型检查。
    • Python是提倡“君子协商”来约束,而不是通过强制约束。
  • 代码示例-强约束示例:

    • # 多态+强约束
      # 优势: 统一性,强制约束
      # 劣势: 强耦合,扩展性差,灵活度不高
      import abc class Animal(metaclass=abc.ABCMeta): # 现在Animal也不能调用了 @abc.abstractmethod def talk(self): # 这就是强约束,必须得有talk方法,否则报错 pass class Dog(Animal): def ttt(self): # 这里叫ttt就报错了 print("汪汪汪~") class Cat(Animal): def talk(self): print("喵喵喵~") class Pig(Animal): def talk(self): print("哼哼哼~") # dog1 = Dog() #若是没有talk方法,就报错 TypeError: Can't instantiate abstract class Dog with abstract methods talk ant = Animal() #这个父类也不能调用了,也不应该调用,现在的Animal是个基准的功能。

       

  • 代码示例-鸭子类型示例:
    • class Dog:
          def talk(self):  
              print("汪汪汪~")
      
      class Cat:
          def talk(self):
              print("喵喵喵~")
      
      class Pig:
          def talk(self):
              print("哼哼哼~")

       

  • 参考资料:
    • https://segmentfault.com/a/1190000041689022
    • https://cloud.tencent.com/developer/article/1484390

33.Python中如何体现鸭子类型的?

  • 比如:字符串,列表,字典,都有len的方法,他们都可以求长度。
  • 就是一种体现鸭子类型的思想。
  • l = [1,2,3,4]
    d = {"name":"liqi","age":18}
    s = "abcde"
    print(l.__len__())  # 4
    print(d.__len__())  # 2
    print(s.__len__())  # 5
    
    print(len(l))   # 4
    print(len(d))   # 2 
    print(len(s))   # 5 
    
    # 这里len函数本质就是调用.__len__()

     

34.内置方法isinstance和issubclass:

  • isinstance是用来判定,某个实例是归属哪个类。
    • 体现Python中一切皆对象(实例)的思想。
    • 跟type函数的功能类似,isinstance能体现程序员Python的功力和认识。(更专业~)
  • issubclass判定,某个类是不是另一个类的子类。
  • 代码示例:
    • class Foo:
          pass
      obj1 = Foo()
      
      print(isinstance(obj1,Foo)) # 判断是obj1是不是Foo的一个实例
      print(isinstance([1,2,4],list)) # 判断[1,2,4]是不是list的一个实例 # 也体现Python的一切皆对象的思想
      
      print(issubclass(Foo,object))   # 判断一个类的父类是不是object

       

35.__开头__结尾的会自动触发运行?

  • 对的,满足某种条件是会自动触发运行。

36.__str__是什么?

  • 当别人打印对象的时候,自动触发运行。
  • 代码示例:
    • # __开头__结尾的方法,会在满足某种条件下自动触发
      # 比如想打印对象的时候展示一些数据,用什么? __str__
      class People:
          def __init__(self,name,age):
              self.name = name
              self.age = age
      
          def __str__(self):
              print("====>")
              return f"名字是:{self.name};年龄是:{self.age}"
          
      xiaoming = People("LIQI",10)
      print(xiaoming) # print(xiaoming.__str__())

       

37.__del__是什么?

  • 当要删除这个对象的时候,触发del 

  • 什么场景需要用del
    • 跟操作系统资源有关的时候,比如文件的打开和关闭。
    • 而应用程序,python自己会释放。这部分不需要关心。
  • 示例代码:
    • # __开头__结尾的方法,会在满足某种条件下自动触发
      # 比如想在删除展示一些数据,用什么? __del__
      class People:
          def __init__(self,name,age):
              self.name = name
              self.age = age
      
          def __del__(self):
              print("==删除==>")
      
      
      xiaoming = People("LIQI",10)
      
      del xiaoming # 不删除也会执行,会在主程序准备释放的时候,执行
      print("=====end===")

       

38.反射是什么?

  • 一种用字符串反向来获取对象的属性或方法。
  • 实例代码:
    • class People:
          country = "china"
      
          def __init__(self, name, age):
              self.name = name
              self.age = age
      
          def tell_info(self):
              print(f"我是{self.name}")
      
      
      stu = People("liqi", 18)
      
      # hasattr 有吗
      # print("country" in stu.__dict__)    # False
      # print("country" in People.__dict__) # True
      
      # 替代上面的两种形式
      # hasattr 会先从对象里找,再从父类里面找
      # print(hasattr(stu,"country"))   # True
      
      # getattr 获取
      
      # print(getattr(stu, "name"))  # liqi
      # f = stu.tell_info  ======== print(f)
      # print(getattr(stu,"tell_info")) # <bound method People.tell_info of <__main__.People object at 0x000001D5BA649400>>
      # f = getattr(stu, "tell_info")  # f = stu.tell_info
      # f()  # 调用 tell_info
      
      # setattr 设置
      # setattr(stu, "iiii", "oooo")
      # print(stu.__dict__)  # {'name': 'liqi', 'age': 18, 'iiii': 'oooo'}
      
      # delattr 删除
      # print(stu.__dict__)
      # delattr(stu, "name")
      # print(stu.__dict__)
  • 应用场景:
    • 比如web路由分发的时候,有种需求是,如何通过字符串"login"来调用login函数?
    • 用getattr就可以解决,也就是用反射来解决。
  • 扩展资料
    •   https://www.liujiangblog.com/course/python/48

 39.异常机制

  • 慎用。
  • try...except
  • 实例代码:
    •   
      try:
          print(111)
          l = [123,456]
          l[10]
      except IndexError as e:
          print("出现错误:",e)
      
      else:   # 有异常就不执行这个,没有异常执行这个
          print("else===")
      print("===========+++++++")

       

 40.自定义异常

  • 继承BaseException
  • 示例代码:
    • class Permission(BaseException):
          pass
      
      raise Permission("权限异常")
      #####
      Traceback (most recent call last):
        File "C:\Users\李琦\PycharmProjects\pythonProject2\0213\异常.py", line 15, in <module>
          raise Permission("权限异常")
      __main__.Permission: 权限异常

       

 

 

 

  • 参考资料
posted @ 2023-02-02 17:17  o蹲蹲o  阅读(32)  评论(0编辑  收藏  举报