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]
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: 权限异常
-
- 参考资料