动静态方法
绑定方法与非绑定方法的使用:若类中需要一个功能,该功能的实现代码中需要引用对象则将其定义成对象方法,需要引用类则将其定义成类方法,无需引用类或对象则将其定义成静态方法
动态方法(绑定方法)
绑定给对象的方法
| ''' |
| 类中定义的函数默认绑定给对象方法 |
| 对象来调用,会把对象自己当成第一个参数传给方法的第一个形参self |
| 类来调用绑定给对象的方法:类名来调用有几个参数填几个参数 |
| ''' |
| class Studen: |
| school_name='清华' |
| |
| def __init__(self,name,age,gender): |
| self.name=name |
| self.age=age |
| self.gender=gender |
| |
| |
| def choice_course(self): |
| print(f'学生{self.name}正在选课') |
| |
| stu=Studen('jack',16,'male') |
| print(stu.__dict__) |
| stu.choice_course() |
| |
| |
| class Student(): |
| school_name='清华' |
| |
| def choice_course(self): |
| print('对象的方法') |
| Student.choice_course(123) |
绑定给类的方法
| ''' |
| 被@classmethod修饰的函数默认绑定给类的方法 |
| 类调用会把类名当成第一个参数传给方法的第一个形参cls |
| 对象调用会自动将产生该对象的类当成第一个参数传入 |
| ''' |
| class Student(): |
| school_name='清华' |
| |
| @classmethod |
| def func(cls): |
| print('猜猜猜',cls) |
| |
| Student.func() |
| |
| obj=Student() |
| obj.func() |
| |
| class Mysql(): |
| port = 3306 |
| ip = '127.0.0.1' |
| |
| @classmethod |
| def from_func(cls): |
| return f'ip:{cls.ip} port:{cls.port}' |
| |
| res=Mysql.from_func() |
| print(res) |
| |
| ip=3306 |
| port='127.0.0.1' |
| |
| |
| import settings |
| class Mysql(): |
| def __init__(self,ip,port): |
| self.ip=ip |
| self.port=port |
| |
| @classmethod |
| def from_func(cls): |
| return cls(settings.ip,settings.port) |
| res=Mysql.from_func() |
| print(res) |
| print(res.port,res.ip) |
静态方法(非绑定方法)
| ''' |
| 为类中某个函数加上装饰器@staticmethod后,该函数就变成了非绑定方法,也称为静态方法,该方法不绑定给对象,也不绑定给类,无论是类还是对象调用它就是一个普通函数,因而没有自动传值则调用时就需要传对应数量的参数 |
| ''' |
| class Student(): |
| def __init__(self, name, age): |
| self.name = name |
| self.age = age |
| self.id = self.get_id() |
| |
| @staticmethod |
| def get_id(): |
| import uuid |
| return uuid.uuid4() |
| |
| stu=Student('kevin',19) |
| print(stu.id) |
| print(stu.create_id()) |
| print(Student.create_id()) |
| import random |
| class Users(): |
| def __init__(self,name,password): |
| self.name=name |
| self.password=password |
| |
| @staticmethod |
| def get_code(n): |
| code='' |
| for i in range(n): |
| random_int = str(random.randint(0, 9)) |
| random_upper = chr(random.randint(65, 90)) |
| random_lower = chr(random.randint(97, 122)) |
| temp =random.choice([random_lower,random_upper,random_int]) |
| code +=temp |
| return code |
| user=Users('kevin',123) |
| print(user.get_code(5)) |
| print(Users.get_code(4)) |
| |
| |
| '''如果方法里面用到了对象,又用到了类,最好绑定给对象''' |
| |
| print(self.__class__) |
| cls = self.__class__ |
三大特征之封装
| 封装:即将某些部分隐藏起来,在程序外部看不到,其含义是其他程序无法调用 |
| |
| 隐藏:将数据和功能隐藏起来,不让用户直接调用,而是开放一些接口间接调用,从而在接口中添加额外的操作 |
| |
| 作用:保护数据安全性、提高代码复用性、降低耦合度等 |
| 在Python中,我们可以通过以下方式来实现封装: |
| |
| 1.在类定义阶段,使用__对属性或方法进行命名,以表示其为私有属性或私有方法,仅允许在类内部使用。 |
| 2.使用@property装饰器将类的方法转换为只读属性。 |
| 3.使用setter和getter函数对类的属性进行封装。 |
隐藏属性
将数据和功能隐藏起来,不让用户直接调用,而是开放一些接口间接调用,从而在接口中添加额外的操作
如何隐藏属性
| ''' |
| 1. 在类定义阶段,名字前面有__那么该名字就被隐藏起来,无法直接访问(本质上隐藏属性发生了变形:_类名__属性名\方法名),通过类名\对象名._类名__数据功能名才能访问 |
| 2. 隐藏属性既可以隐藏类属性、类(对象)方法、对象属性 |
| 3. 隐藏对外不对内,在类的内部没有隐藏,通过正常的调用方法来调用即可 |
| ''' |
| class Myclass: |
| school_name='清华' |
| |
| _='嘿嘿嘿' |
| _name='tony' |
| __age=18 |
| |
| def __init__(self,name,age): |
| self.__name=name |
| self.__age=age |
| |
| def __choice_course(self): |
| print('baby正在选课') |
| |
| def get_info(self): |
| self.__choice_course() |
| |
| print(Myclass._Myclass__age) |
| Myclass.__hobby='music' |
| print(Myclass.__hobby) |
| obj =Myclass() |
| obj.get_info() |
| obj.__addr='上海' |
| print(obj.__addr) |
| '''在类调用阶段给类添加属性给对象增加数据的方式,使用__是无法隐藏的,依然可以得到结果,只有在类定义阶段名字加__才能被隐藏''' |
开放接口
| ''' |
| 由于隐藏属性是对外不对内的,我们要在内部开放一个接口来返回类内部被隐藏的属性,可以更好的对外做限制 |
| ''' |
| class Person: |
| def __init__(self,name,age,hobby): |
| self.__name=name |
| self.__age=age |
| self.__hobby=hobby |
| |
| def get_info(self): |
| |
| print(f""" |
| 姓名:{self.__name} |
| 年龄:{self.__age} |
| 爱好:{self.__hobby} |
| """) |
| |
| |
| def set_name(self,new_name): |
| if len(new_name)==0: |
| raise ValueError('名字不能为空') |
| if new_name.isdigit(): |
| raise ValueError('名字不能是数字') |
| self.__name=new_name |
| |
| obj=Person('jason',18,'read') |
| obj.get_info() |
| |
| |
| |
| |
| obj.set_name('') |
| obj.set_name('tony') |
| obj.get_info() |
| |
| |
| |
| |
| |
| class Student(): |
| __country='CHN' |
| |
| def __init__(self,name,age): |
| self.name=name |
| self.age=age |
| |
| def set_country(self,country_name): |
| if not isinstance(country_name,str) or not country_name.isalpha(): |
| print('格式错误') |
| return |
| Student.__country=country_name |
| |
| def get_country(self): |
| return self.__country |
| |
| stu=Student('kevin',18) |
| stu.set_country('1') |
| print(stu.get_country()) |
| stu.set_country('China') |
| print(stu.get_country()) |
伪装property装饰器
| 伪装:把类里面的方法伪装成类里面的属性 |
| 如何伪装:被@property修饰的方法就变成伪装属性 |
| @property把方法伪装成属性之后,以后再调用方法的时候,就不用加括号了,只需要对象点属性名即可 |
| eg: |
| class C: |
| def func(self):pass |
| obj=C() |
| obj.func()#没被伪装调用方法 |
| obj.func#经过伪装的调用方法 |
- 伪装方式一

| ''' |
| 因被@property修饰的方法对象无法进行修改删除,若想删除修改则需要额外在删除修改的函数方法上添加以下装饰器 |
| 修改:@被伪装属性的方法名.setter |
| 删除:@被伪装属性的方法名.deleter |
| ''' |
| class Student(): |
| __country = 'CHINA' |
| __city = 'Shanghai' |
| |
| def __init__(self, name, age): |
| self.__name = name |
| self.age = age |
| |
| @property |
| def country(self): |
| return self.__country |
| |
| @country.setter |
| def country(self, country_name): |
| if not type(country_name) is str: |
| return |
| Student.__country = country_name |
| |
| @country.deleter |
| def country(self): |
| del Student.__country |
| |
| stu=Student('KEVIN',19) |
| print(stu.country) |
| stu.country='china' |
| print(stu.country) |
| del stu.country |
| """这种方式,是有顺序要求的,必须是以获取>设置>删除为顺序""" |
| class Student(): |
| __country = 'China' |
| __city = 'shanghai' |
| |
| def __init__(self, name, age): |
| self.__name = name |
| self.age = age |
| |
| def get_country(self): |
| return self.__country |
| |
| def set_country(self,country_name): |
| if not isinstance(country_name,str) or not country_name.isalpha(): |
| print('格式有误') |
| return |
| Student.__country=country_name |
| |
| def del_country(self): |
| del Student.__country |
| print('删除成功') |
| |
| country = property(get_country, set_country, del_country) |
| |
| stu=Student('KEVIN',18) |
| print(stu.country) |
| stu.country='1' |
| print(stu.country) |
| del stu.country |
| class Bmi(): |
| def __init__(self, weight, height): |
| self.weight = weight |
| self.height = height |
| |
| @property |
| def bmi(self): |
| return self.weight / self.height ** 2 |
| stu = Bmi(70, 1.8) |
| |
| print(stu.bmi) |
三大特征之继承
| """ |
| 1. 什么是继承? |
| 继承就是新建类的一种方式,新建出来的类被称为子类或者派生类,被继承的类称为父类或者基类 |
| 子类可以继承父类的所有属性和方法 |
| 2. 为什么要用继承? |
| 类:解决了对象与对象之间的代码冗余问题 |
| 继承:解决了类与类之间的代码冗余问题 |
| 子类就拥有了父类的所有数据和方法 |
| 3. 怎么使用继承? |
| 经典类:没有继承object类的子子孙孙类都是经典类 |
| 新式类:继承了object类的子子孙孙类都是新式类 |
| 只有在python2中才区分经典类和新式类,在python3中,全部都是新式类 |
| 1. 在子类括号里面写上继承的父类名字,括号内可以写多个继承的父类 |
| 2. 子类可以遗传父类的所有属性和方法 |
| 3. 子类继承父类之后,可以使用父类的所有方法和属性,父类不能使用子类的 |
| |
| 4. 查看继承的类 |
| print(类名.__bases__) |
| |
| 5. 继承后对象属性查找顺序 |
| 对象自身>产生对象的类>产生对象的类所继承的类 |
| """ |
| class Student(): |
| school='SH' |
| def __init__(self,name,age,gender): |
| self.name=name |
| self.age=age |
| self.gender=gender |
| |
| def course(self): |
| print(f'{self.name}在选课') |
| |
| class Teacher(): |
| school='SH' |
| def __init__(self,name,age,gender,level): |
| self.name=name |
| self.age=age |
| self.gender=gender |
| self.level=level |
| |
| def score(self): |
| print(f'{self.name}在评分' |
| |
| class People(): |
| school='SH' |
| def __init__(self,name,age,gender): |
| self.name=name |
| self.age=age |
| self.gender=gender |
| |
| class Student(People): |
| def __init__(self, name, age, gender): |
| People.__init__(self, name, age, gender) |
| |
| def course(self): |
| return f'{self.name}在选课' |
| |
| class Teacher(People): |
| def __init__(self,name,age,gender,level): |
| People.__init__(self, name, age, gender) |
| self.level=level |
| |
| def score(self): |
| return f'{self.name}在评分' |
| |
| stu=Student('KEVIN',18,'MALE') |
| print(stu.course()) |
| teacher=Teacher('tony',38,'MALE',10) |
| print(teacher.score()) |
单继承下的属性查找
| 单继承子类只继承一个父类,括号中的类名只能写一个 |
| ''' |
| 单继承下的属性查找顺序: |
| 1、先从对象自己的名称空间中查找 |
| 2、找不到再从产生这个对象的类中查找 |
| 3、类中找不到再去继承的父类中查找 |
| 一直找到没有父类为止,再找不到就报错了 |
| ''' |
| class Foo(): |
| def f1(self): |
| print('from Foo.f1') |
| |
| def f2(self): |
| print('from Foo.f2') |
| self.f1() |
| |
| class Bar(Foo): |
| def f1(self): |
| print('from Bar.f1') |
| obj = Bar() |
| obj.f2() |
| >>from Foo.f2 |
| from Bar.f1 |
| class Foo(): |
| def __f1(self): |
| print('from Foo.f1') |
| |
| def f2(self): |
| print('from Foo.f2') |
| self.__f1() |
| |
| class Bar(Foo): |
| def __f1(self): |
| print('from Bar.f1') |
| |
| obj = Bar() |
| obj.f2() |
| |
| >>from Foo.f2 |
| from Bar.f1 |


多继承下的属性查找


| #多继承就是括号里面可以有多个父类,继承的父类中也可以有多个父类 |
| 非菱形查找:按照继承类从从左往右的顺序一个个分支去查找 |
| 菱形查找: |
| 1. 经典类(仅py2中存在) |
| # 查找顺序:多继承情况下 在要查找属性不存在时,会按照深度优先的方式查找,从继承的类中从左往右依次查找,从第一个分支一直找到顶点 |
| 不继承object或者其子类的类 |
| 2. 新式类(python3) |
| # 查找顺序:多继承情况下 在要查找属性不存在时,会按照广度优先的方式查找,从继承的类中从左往右依次查找,从最后一个分支找到顶点 |
| 所有类默认都继承object |
| class G: # 在python2中,未继承object的类及其子类,都是经典类 |
| def test(self): |
| print('from G') |
| |
| class E(G): |
| def test(self): |
| print('from E') |
| |
| class F(G): |
| def test(self): |
| print('from F') |
| |
| class B(E): |
| def test(self): |
| print('from B') |
| |
| class C(F): |
| def test(self): |
| print('from C') |
| |
| class D(G): |
| def test(self): |
| print('from D') |
| |
| class A(B,C,D): |
| # def test(self): |
| # print('from A') |
| pass |
| |
| obj = A() |
| obj.test() # 如上图,查找顺序为:obj->A->B->E->G->C->F->D->object |
| # 可依次注释上述类中的方法test来进行验证,注意请在python2.x中进行测试 |
| |
| 在py3中查找 |
| class G(object): |
| def test(self): |
| print('from G') |
| |
| class E(G): |
| def test(self): |
| print('from E') |
| |
| class F(G): |
| def test(self): |
| print('from F') |
| |
| class B(E): |
| def test(self): |
| print('from B') |
| |
| class C(F): |
| def test(self): |
| print('from C') |
| |
| class D(G): |
| def test(self): |
| print('from D') |
| |
| class A(B,C,D): |
| # def test(self): |
| # print('from A') |
| pass |
| |
| obj = A() |
| obj.test() # 如上图,查找顺序为:obj->A->B->E->C->F->D->G->object |
| # 可依次注释上述类中的方法test来进行验证 |
| |
super关键字
派生的核心思想是子类继承了父类还可以增加新的功能
| #用super调父类的方法super().__init__(name,age,gender),自己在拓展增加写新的功能self.sid=sid |
| class People(): |
| def __init__(self,name,age,gender): |
| self.name=name |
| self.age=age |
| self.gender=gender |
| |
| class Student(People): |
| def __init__(self,name,age,gender,sid): |
| super().__init__(name,age,gender)#子类调用父类的方法 |
| self.sid=sid |
| |
| class Teacher(People): |
| def __init__(self,name,age,gender,level): |
| super().__init__(name,age,gender)#子类调用父类的方法 |
| self.level=level |
| |
| stu1=Student('kevin',19,'male',11) |
| print(stu1.__dict__)#{'name': 'kevin', 'age': 19, 'gender': 'male', 'sid':11} |
| tea1=Teacher('tony',29,'male',1) |
| print(tea1.__dict__)#{'name': 'tony', 'age': 29, 'gender': 'male', 'level':1} |
| class People(): |
| school = 'SH' |
| |
| def __init__(self, name, age, gender): |
| self.name = name |
| self.age = age |
| self.gender = gender |
| |
| |
| class Student(People): |
| def __init__(self, name, age, gender, course=None): |
| if course is None: |
| course = [] |
| super(Student, self).__init__(name, age, gender) |
| ''' |
| 1、这样的调用方式,其实是不依赖于继承的,指名道姓的调用父类__init__方法 |
| People.__init__(self, name, age, gender) |
| 2、在py3中的写法,super依赖于继承,self参数不写 |
| super().__init__(name, age, gender) |
| 3、在py2中的写法,super依赖于继承 |
| super(Student, self).__init__(name, age, gender) |
| 以上三种方法使用其中一种即可 |
| super(Student, self) 返回的是一个特殊对象 |
| super关键字的查找顺序是按照类的mro列表顺序依次查找 |
| ''' |
| self.courses = course |
| |
| def choose_course(self): |
| pass |
| |
| tea=Teacher('yy',19,'male',10) |
| print(tea.__dict__) |
| stu=Student('kk',19,'male') |
| print(stu.__dict__) |
| stu.course.append('python') |
| print(stu.__dict__) |
| class Mylist(list): |
| def append(self,values): |
| if values =='name': |
| print('name不能追加') |
| return |
| |
| super().append(values) |
| print(f'{values}追加成功') |
| |
| obj=Mylist() |
| print(obj,type(obj)) |
| obj.append('name') |
| obj.append(123) |
| obj.append(456) |
| print(obj) |
mro列表
用于解决多继承时方法调用顺序问题
| ''' |
| 遇到多继承中出现了super关键字,如何更准确的知道它的查找顺序,我们需要打印出类的mro列表; |
| 输出mro列表:类名.mro()\类名.__mro__ |
| 查看一个类的mro列表, 看一个类的mro列表,要从起始类开始看 |
| ''' |
| class A(): |
| def test(self): |
| super().test() |
| |
| class B(): |
| def test(self): |
| print('from B') |
| |
| class C(A,B): |
| pass |
| |
| c=C() |
| c.test() |
| print(C.mro()) |
| |
| a=A() |
| print(A.mro()) |
| a.test() |
Mixins机制
Python提供了Mixins机制,简单来说Mixins机制指的是子类混合(mixin)不同类的功能,而这些类采用统一的命名规范(例如Mixin后缀),以此标识这些类只是用来混合功能的
| class Vehicle: |
| def fly(self): |
| ''' |
| 飞行功能相应的代码 |
| ''' |
| print("I am flying") |
| |
| class CivilAircraft(Vehicle): |
| pass |
| |
| class Helicopter(Vehicle): |
| pass |
| |
| class Car(Vehicle): |
| pass |
如上所示民航飞机、直升飞机、轿车都是一个(is-a)交通工具,前两者都有一个功能是飞行fly,但是轿车显然不应该拥有此功能
| class Vehicle: |
| pass |
| |
| class FlyableMixin: |
| def fly(self): |
| ''' |
| 飞行功能相应的代码 |
| ''' |
| print("I am flying") |
| |
| class CivilAircraft(FlyableMixin, Vehicle): |
| pass |
| |
| class Helicopter(FlyableMixin, Vehicle): |
| pass |
| |
| class Car(Vehicle): |
| pass |
| |
| |
上面的CivilAircraft、Helicopter类实现了多继承,不过它们继承的第一个类我们起名为FlyableMixin,这个类是一个Mixin类表示其作为功能的辅类,而不是作为主类
三大特征之多态
多态与多态性
多态指的是一类事物有多种形态,比如动物有多种形态:猫、狗、猪;比如水有多种形态:液态、气态、固态
多态性指的是可以在不用考虑对象具体类型的情况下而直接使用对象,这就需要在设计时,把对象的使用方法统一成一种:例如cat、dog、pig都是动物,但凡是动物肯定有talk方法,于是我们可以不用考虑它们三者的具体是什么类型的动物,而直接使用
| class Animal(): |
| def speak(self): |
| pass |
| |
| class People(Animal): |
| def speak(self): |
| print('你好') |
| |
| class Cat(Animal): |
| def speak(self): |
| print('喵喵') |
| |
| stu=People() |
| stu.speak() |
| >>你好 |
多态性的本质在于不同的类中定义有相同的方法名,这样我们就可以不考虑类而统一用一种方式去使用对象,可以通过在父类引入抽象类的概念来硬性限制子类必须有某些方法名
| 强制约束 |
| """ |
| 1.父类只能被继承不能被调用产生对象 |
| 2.父类里面的方法不再实现具体的功能,父类属性和方法不是让子类来继承的 |
| 3.定义继承父类的子类中一定要有父类这个方法,抽象类中的父类强制限制子类的行为 |
| """ |
| |
| import abc |
| |
| class Animal(metaclass=abc.ABCMeta): |
| @abc.abstractmethod |
| def speak(self): |
| pass |
| |
| class People(Animal): |
| def speak(self): |
| print('你好') |
| |
| class Cat(Animal): |
| def speak(self): |
| print('喵喵') |
| |
| obj=Animal() |
| obj=People() |
| obj.speak() |
鸭子类型
| 不强制约束(鸭子类型) |
| "鸭子类型:它是一种动态的编程风格,我们更多关注的是对象的方法或属性,而不是对象的类型,我们不用在使用type或者isinstance查出它的类型了,通过多态提高了灵活性,多态带来的特性在不考虑对象的数据类型下,直接调用的对应方法" |
| |
| class People(): |
| def speak(self): |
| print('你好') |
| |
| class Dog(): |
| def speak(self): |
| print('旺旺') |
| |
| class Cat(): |
| def speak(self): |
| print('喵喵') |
| |
| def animal(obj): |
| return obj.speak() |
| |
| xiaohong=People() |
| xiaohuang=Dog() |
| xiaoju=Cat() |
| |
| animal(xiaohong) |
| animal(xiaohuang) |
| animal(xiaoju) |
魔法
Python的Class机制内置了很多特殊的方法来帮助使用者高度定制自己的类,这些内置方法都是以双下划线开头和结尾的,会在满足某种条件时自动触发
| """当Python实例化一个对象时,首先调用__new__()方法构造一个类的实例,并为其分配对应类型的内存空间,该实例的内存地址就是它的唯一标识符。然后再调用__init__()方法对实例进行初始化,通常是对该实例的属性进行初始化""" |
| class Person(object): |
| |
| def __new__(cls): |
| print("__new__ called") |
| return super().__new__(cls) |
| |
| def __init__(self): |
| print("__init__ called") |
| |
| a = Person() |
| |
| """输出结果 |
| __new__ called |
| __init__ called |
| """ |
| '''初始化方法,调用类的时候自动触发,给对象添加数据时自动触发,里面有一个self参数,用来接收对象的''' |
| class C(object): |
| def __init__(self,name): |
| self.name=name |
| print('init') |
| obj=C('kevin') |
| """1.打印对象或者输出对象时自动触发 |
| 2.必须有返回值,且返回值为字符串类型,否则报错 |
| 3.若两者同时存在类中,只会执行__str__,执行优先级高于__repr__ |
| 4.一般情况都使用__str__,偶尔会遇到__repr__ |
| """ |
| class Student(): |
| def __init__(self, name, age): |
| self.name = name |
| self.age = age |
| |
| def __repr__(self): |
| return 'repr执行了' |
| |
| |
| def __str__(self): |
| print('我执行了!!!') |
| return '字符串类型' |
| |
| stu = Student('kevin', 20) |
| print(stu) |
| print('from repr',repr(stu)) |
| print('from str',str(stu)) |
| """1.删除对象的时候自动触发 |
| 2.当程序全部执行完毕,也会自动调用触发 |
| 3.一般用在回收系统资源的时候使用""" |
| class Student(): |
| def __init__(self, name, age): |
| self.name = name |
| self.age = age |
| |
| def __del__(self): |
| print('触发了') |
| |
| tu = Student('kevin',20) |
| del stu |
| print(123) |
| '''当对象被当做with上下文管理操作的开始自动触发,并且该方法返回什么,as后面的变量名就接收到什么''' |
| class Open(): |
| def __init__(self,name): |
| self.name=name |
| |
| def __enter__(self): |
| print('首先执行enter') |
| return '我返回啥f就接收啥' |
| |
| def __exit__(self, exc_type, exc_val, exc_tb): |
| print('最后执行exit') |
| |
| with Open('a.txt') as f: |
| print('其次执行with语句') |
| print(f) |
| """输出结果 |
| 首先执行enter |
| 其次执行with语句 |
| 最后执行exit |
| 我返回啥f就接收啥 |
| """ |
| '''当对象参与的with语句执行完毕后自动触发''' |
| class Open: |
| def __init__(self, name): |
| self.name = name |
| |
| def __enter__(self): |
| print('出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量') |
| |
| def __exit__(self, exc_type, exc_val, exc_tb): |
| print('with中代码块执行完毕时执行我啊') |
| print(exc_type) |
| print(exc_val) |
| print(exc_tb) |
| |
| |
| with Open('a.txt') as f: |
| print('=====>执行代码块') |
| raise TypeError('***着火啦,救火啊***') |
| print('0'*100) |
总结:__enter__,__exit__
1、出现with语句,首先对象的__enter__被触发,有返回值则赋值给as声明的变量
2、其次开始执行with代码块
3、with中代码块执行完毕后再执行__exit__方法
4、当with代码块出现异常,在__exit__方法中能够得到异常的信息(异常类型、异常值、追溯信息)
如果在__exit__方法中没有return True,则with语句后的代码块不再执行;当有return True时不再抛出异常,with语句后的代码块正常执行
__getattr__,__setattr__,__delattr__
| """__getattr__当调用属性且使用句点符查找不存在属性时触发 |
| __setattr__当使用句点符修改属性或者增加属性值时自动触发,因此不能再该方法内用句点符方式赋值 self.name = value 会死循环,而是用self.__dict__[key] =value方式增加属性 |
| __delattr__当使用句点符删除属性时会自动触发,因此不能再该方法内用句点符方式删值 sdel self.item 会死循环,而是用self.__dict__.pop(item)方式删除属性""" |
| |
| class Foo: |
| x = 1 |
| |
| def __getattr__(self, item): |
| print('item:%s' % item) |
| print('----> from getattr:你找的属性不存在') |
| |
| def __setattr__(self, key, value): |
| print('----> from setattr') |
| print(key,value) |
| |
| self.__dict__['z']=2 |
| |
| def __delattr__(self, item): |
| print('----> from delattr') |
| |
| self.__dict__.pop('z') |
| obj =Foo(1) |
| obj.z =1 |
| del obj.z |
| print(obj.__dict__) |
__setitem__,__getitem__,__delitem__
| """__getitem__当属性通过中括号取值时会自动触发 |
| __setitem__当属性通过中括号赋值时自动触发 |
| __delitem__当属性通过中括号删除值时自动触发""" |
| class Foo: |
| def __init__(self, name): |
| self.name = name |
| |
| def __getitem__(self, item): |
| print(item) |
| print('__getitem__执行了') |
| print(self.__dict__[item]) |
| |
| |
| def __setitem__(self, key, value): |
| print(key, value) |
| print('__setitem__执行了') |
| self.__dict__[key] = value |
| |
| def __delitem__(self, key): |
| print('del obj[key]时,我执行') |
| self.__dict__.pop(key) |
| |
| def __delattr__(self, item): |
| print('del obj.key时,我执行') |
| self.__dict__.pop(item) |
| |
| obj=Foo('kevin') |
| print(obj.name) |
| print(obj['name']) |
| obj['name'] |
| obj['age']=19 |
| del obj['age'] |
| """当给对象加括号调用时自动触发,__call__方法返回什么对象调用之后就返回什么 |
| 后续课程flask框架源码的时候,__call__方法就是入口 |
| """ |
| class Foo: |
| def __init__(self): |
| pass |
| |
| def __call__(self, *args, **kwargs): |
| print('__call__执行了') |
| print(args,kwargs) |
| return 123 |
| |
| obj1=Foo() |
| print(obj1(123,321,name='kevin')) |
| """输出结果 |
| __call__执行了 |
| (123, 321) {'name': 'kevin'} |
| 123 |
| """ |
| """判断对象是否为某个类的实例,判断某个数据值是不是某个数据类型""" |
| class Student(): |
| def __init__(self,name): |
| self.name=name |
| stu =Student('kevin') |
| print(isinstance(stu,Student)) |
| print(isinstance('123',str)) |
| """判断一个类是否某个类的子类""" |
| class Student(object): |
| def __init__(self,name): |
| self.name=name |
| stu =Student('kevin') |
| print(issubclass(Student,object)) |
| """查看类内部的注释""" |
| class Foo: |
| """这是注释""" |
| pass |
| |
| class Bar(Foo): |
| pass |
| |
| print(Foo.__doc__) |
| print(Bar.__doc__) |
反射
在Python中,反射指的是'利用字符串来操作对象的属性和方法
| 涉及到四个内置函数的使用: |
| 1.hasattr() |
| 2.getattr() |
| '''如果第二个参数是属性的话,直接返回属性的值 |
| 如果属性不存在会报错 |
| 如果写了第三个参数,当属性不存在的时候,会把第三个值返回 |
| 如果第二个参数是函数名,那么返回的就是函数内存地址,如果想调用直接返回结果加括号 |
| ''' |
| 3.setattr() |
| """ |
| 给对象增加属性, 属性名不存在则增加属性 |
| 给对象增加属性,属性名存在则修改属性 |
| """ |
| 4.delattr() |
| class C1: |
| school_name='小姐姐学院' |
| |
| def choice_course(self): |
| print('大宝贝在选课') |
| |
| obj =C1() |
| |
| print(hasattr(obj,'school_name')) |
| print(getattr(obj,'school_name')) |
| print(getattr(obj,'choice_course')) |
| print(getattr(obj,'schoolname')) |
| print(getattr(obj,'school_name',999)) |
| print(getattr(obj,'schoolname','不存在')) |
| |
| setattr(obj,'name','jason') |
| print(obj.__dict__) |
| setattr(obj,'name','123') |
| print(obj.__dict__) |
| |
| delattr(obj,'name') |
| print(obj.__dict__) |
| delattr(obj,'school_name') |
| |
| input输入的是字符串类型,而属性名方法名变量名却不是字符串类型,反射则可以解决此问题利用字符串来操作对象的数据和方法 |
| class C1(): |
| school_name='小姐姐学院' |
| |
| def choice_course(self): |
| print('大宝贝在敲代码') |
| |
| obj =C1() |
| |
| while True: |
| target_name =input('请输入想要操作的名字:').strip() |
| if hasattr(obj,target_name): |
| print('系统中有该方法') |
| data_or_func =getattr(obj,target_name) |
| if callable(data_or_func): |
| print('本次执行的是%s方法'% target_name) |
| data_or_func() |
| else: |
| print('本次执行的是%s属性'% target_name) |
| print(data_or_func) |
| |
| else: |
| print('系统中没有该名字') |
| |
| |
| 请输入想要操作的名字:xxx |
| 系统中没有该名字 |
| 请输入想要操作的名字:school_name |
| 系统中有该方法 |
| 本次执行的是school_name属性 |
| 小姐姐学院 |
| 请输入想要操作的名字:choice_course |
| 系统中有该方法 |
| 本次执行的是choice_course方法 |
| 大宝贝在敲代码 |
| |
| settings.py文件 |
| NAME='kevin' |
| AGE=18 |
| desc= ABC |
| info= 123 |
| |
| 执行文件 |
| import settings |
| print(dir(settings)) |
| |
| useful_dict={} |
| for name in dir(settings): |
| if name.isupper(): |
| useful_dict[name] =getattr(settings,name) |
| print(useful_dict) |
| |
| |
| while True: |
| target_name=input('输入方法名') |
| if hasattr(settings, target_name): |
| print(getattr(settings, target_name)) |
| else: |
| print('该模块没有该方法名') |
| class FtpServer: |
| def serve_forever(self): |
| while True: |
| inp = input('input your cmd>>: ').strip() |
| cmd, file = inp.split() |
| |
| |
| |
| if hasattr(self, cmd): |
| |
| |
| func = getattr(self, cmd) |
| func(file) |
| |
| def get(self, file): |
| print('Downloading %s...' % file) |
| |
| def put(self, file): |
| print('Uploading %s...' % file) |
| |
| server = FtpServer() |
| server.serve_forever() |
| 1. 以上4个方法中得第一个参数也可以写模块名 |
| import time |
| time.sleep(3) |
| |
| res=getattr(time,'sleep') |
| print(res)#<built-in function sleep> |
| res(3)#调用sleep函数 |
| |
| 2. __import__ |
| # 通过字符串的形式导入模块 |
| time=__import__('time') |
| time.sleep() |
| |
| random = __import__("random") |
| res=random.randint(0,9) |
| print(res)#7 |
| |
| class Animal(): |
| def speak(self): |
| raise Exception('必须实现speak方法') |
| |
| class People(Animal): |
| def speak(self): |
| pass |
| |
| obj=People() |
| obj.speak() |
元类
简介:即产生类的类
下示得出结果所有类都为type类产生,这个类称为元类
| print(type(123)) |
| print(type([123])) |
| print(type(int)) |
| print(type(list)) |
| |
| class A(): |
| pass |
| print(type(A)) |
| |
| print(type(type)) |
| |
| "综上得出结果所有类都为type类产生,这个类称为元类" |

产生类的两种方式
-
class关键字
class关键字在帮我们创建类时,必然帮我们调用了元类,那调用type时传入的参数是什么呢?必然是类的关键组成部分,一个类有三大组成部分
| 1、类名class_name='StanfordTeacher' |
| |
| 2、基类们class_bases=(object,) |
| |
| 3、类的名称空间class_dic,类的名称空间是执行类体代码而得到的 |
| |
| |
| class StanfordTeacher(): |
| pass |
-
type关键字
由于所有类都是有type类造出来的,所以我们也可以使用type类造出来一个新类
| type(object_or_name, bases, dict) |
| |
| type("类名", "父类", "类的名称空间") |
自定义元类
元类是造出类的类,所以,我们可以对类进行高度的定制化
| 举个例子:我让你写出来的类名必须是首字母大写? |
| 思考:类是如何产生的?类是由元类产生的type类,如果想改类的行为,应该在元类里的__init__方法中改. |
| 对象是如何产生的? 是执行了产生这个对象的类内部的__init__方法 |
| |
| 推导:想改父类的__init__方法, 但是又不能修改源代码,所以我们可以写出来一个子类来继承父类type,在子类里面写代码,然后再重新执行一些父类的__init__方法. |
| |
| class MyTypeClass(type): |
| def __init__(self, cls_name, cls_bases=None, cls_dict=None): |
| print(cls_name, cls_bases, cls_dict) |
| |
| |
| if not cls_name.istitle(): |
| raise Exception("类名必须是首字母大写") |
| super().__init__(cls_name, cls_bases=None, cls_dict=None) |
| class C1(metaclass=MyTypeClass): |
| school = 'Sh' |
| |
| class a(metaclass=MyTypeClass): |
| pass |
| |
| |
| obj = a() |
| |
| 思考:对象加括号会自动调用类的__call__方法,并且,__call__方法里面返回什么,那么,对象加括号的位置就是什么,类名()会怎么样? |
| 推导:类名(),应该是会执行产生类的类中的__call__方法,而这个类恰好是元类type |
| class MyClass(type): |
| def __call__(self, *args, **kwargs): |
| print("__call__ 执行了") |
| print(args, kwargs) |
| |
| |
| if args: |
| raise Exception("传参必须是关键字传参") |
| |
| super(MyClass, self).__call__(*args, **kwargs) |
| |
| |
| class C1(metaclass=MyClass): |
| def __init__(self, name): |
| print("__init__ 执行了") |
| |
| |
| """看C1类里面的__init__方法和元类里面的__call__方法的执行顺序""" |
| |
| |
| obj = C1(name='kevin') |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库