【0】习题
1.列出你所知道的魔术方法,分别在什么时候执行
1.__init__在对象实例化时
2.__str__是在打印对象显示内容的时候自动触发
3.__del__程序正常运行结束时被动删除,使用del关键字主动删除,
4.__getattr__对象查找不存在的名字的时候触发
5.__setattr__对象在执行添加属性操作时自动触发
6.__call__对象被加括号调用的时候自动触发obj()
7.__enter__对象被执行with(上下文管理)语法开始自动触发
该方法返回什么,as后面就会得到什么
8.__exit__对象被执行with(上下文管理)语法结束自动触发
9.__getattribute__只要对象查找名字,无论名字存不存在都会执行该方法
如果有__getattribute__就不会执行__getattr__
10.__repr__ : 交互解释器会触发
eg:在交互解释器中创建一个Person对象 就会
11.__doc__ : 打印类里面的注释内容的时候会触发
eg:用help()查看
12.__setitem__ : 对象[key]=value 设置值的时候会触发--》中括号设置属性的时候触发
13.__getitem__ : 获取属性的时候会触发 获取方式为 对象[key]
14.__delitem__ : 删除属性的时候会触发 删除方式为 del 对象[key]
2.反射有什么用,如何用
涉及到对象和字符串一定用反射
1.hasattr()==>判断类中有没有字符串对应的数据或函数
2.getattr()==>根据字符串获取对应把你属性值或函数地址<function Student.get at 0x10527a8c8>,函数的运行值
3.setattr()==>根据字符串给的对象设置键值对,新增,键值对,第二个是键,第三个是值
语法:setattr(对象名,要删的函数名或属性名即名称空间字典中的键,要换的函数名或属性名(变量))
注意:有则修改,无则新增
4.delattr()==>根据字符串删除对象对应的键值对==》给键
语法:delattr(对象名,要删的函数名或属性名即名称空间字典中的键)
【一】绑定方法
在类中定义的函数默认都是绑定给对象使用的
即对象来调 会自动将对象当做第一个参数传入
self自动传入当作对象
【二】init魔法方法推导过程
# 推导步骤1 一步步自己手动添加
obj1.__dict__['name'] = 'jason'
obj2.__dict__['name'] = 'kevin'
# 推导步骤2 封装成函数减少代码冗余
def set_info(obj, name):
obj.__dict__['name'] = name
# 推导步骤3 将函数整合到类体代码中
class Student:
def set_info(obj, name):
obj.name = name
# 推导步骤4 python提供自动出发的方法
class Student:
def __init__(obj, name):
obj.name = name
__init__如何执行
1.类加括号先产生一个空对象
2.将类括号内的参数和空对象交给__init__执行
空对象作为第一个参数
3.该方法会给对象添加独有的数据并自动返回对象本身
【三】动态静态方法
1.动态(自动调用)两种(不用传参)
a.绑定给对象(普通)
对象调用绑定给对象的方法:会自动将对象当做第一个参数传入self
# (1)对象可以直接调用绑定给对象的方法
s = Student('dream')
s.talk() # 默认将 s 作为 self 自动传入
# (2)类调用绑定给对象的方法,需要主动传入一个生成的对象
Student.talk(s)
b.绑定给类
类调用绑定给类的方法:会自动将类当做第一个参数传入cls
eg:class Student:
@classmethod
def eat(cls):
prtin(cls)
# (1)对象调用绑定给类的方法, 默认将实例化得到当前对象的类自动传入
stu.read('dream')直接调
# (2)类调用绑定给类的方法,类可以直接调用,默认将调用当前方法的类自动传入
Student.read('dream')
2.静态方法(就是普通的函数)有几个参就传几个参数两种
a.普通函数
b.类中@staticmethod
class Student:
@staticmethod
def speak(a):
print(a)
【四】继承
'''以后看到self点东西 一定要问自己self是谁'''
'''需明确self就是当前实例化的对象'''
1.就是获得父亲的所有东西
2.使用:class A(B): #B是父类,A是B的子类
pass
class A(B,C,D):
pass
3.一个类可以继承多个父类
一个类也可以被多个子类继承
4.在面向对象编程中 其实类和父类的主要功能都是用来减少代码冗余的
对象:数据与功能的结合体
类:多个对象相同数据和功能的结合体
父类:多个类相同数据和功能的结合体
5.关于继承是名字的查找顺序
'''名字的查找顺序永远都是 先从当前对象自身开始查找'''
a.单继承==》对象 >>> 类 >>> 父类
b.多继承===》
没有形成闭环的情况下,深度优先遍历
形成闭环式先先用深度走完没闭环最后走那个点
6.MRO决定了在多继承情况下,方法和属性的解析顺序。
【五】派生类
1.子类调用父类的初始化方法,但是子类中的属性要比父类多怎么办
def __init__(self,写上你所有需要的属性)
完整写法《==》super(自己类名,self).__init__(父类中你需要的属性)
精简写法《==》super().__init__(父类中你需要的属性)
父类中没有你需要的属性也要写上
self.属性名=属性值
如果自己写的子类需要使用父类的方法 并且还需要基于该方法做扩展
这样的子类我们称之为派生类(本质还是子类)
那么可以使用super关键字来实现
语法:
super().方法名(父类中你需要东西)
主要记住super是在父类的基础上进行扩展
class Myclass(list):
def append(self,args):
if args=='111':
print("对不起,不能追加")
else:
super(Myclass,self).append(args)
s=Myclass()
s.append("111")
print(s)
为什么s直接就是列表?
首先明确init方法是自动调用的
在你实例化对象时,类中没有init那么就会去父类中看是否有init然后自动调用
他就变成列表了
【六】继承下的派生实际应用
# 改写json,datatime对象写不进去
import json
import datetime
from typing import Any
class modify(json.JSONEncoder):
def default(self, o: Any) -> Any:
if isinstance(o,datetime.datetime):
return o.strftime("%Y-%m-%d %X")
elif isinstance(o,datetime.date):
return o.strftime("%Y-%m-%d")
else:
super().default(o)
【七】封装
1.把一些东西(属性,函数)藏起来不让别人看见,不让外界调用--》eg:__name
利用双下划线
2.其实就是变形了--》在外面__属性也获得不了,查看名称空间__dict__==》他变形成了_类名__属性,但不允许在外部这样查看
3.并没有真正的隐藏 而是自动转换成了特定的语法
我们虽然指定了封装的内部变形语法 但是也不能直接去访问
看到了就表示这个属性需要通过特定的通道(接口)去访问
4.那么不允许直接利用名称空间访问以及操作,就要在类内提供响应的接口(功能函数)让外部间接的操作数据,在接口里可以直接__属性进行改值,查看等操作
eg:
a.函数
class Atm():
def __card(self):
print("请插卡")
def__auth(self):
print("用户认证")
def withdraw(self):
self.__card()
self.__auth()
obj=Atm()
obj.withdraw()
b.属性
class Student(object):
__school = '清华大学'
def __init__(self, name, age):
self.__name = name
self.__age = age
# 专门开设一个访问学生数据的通道(接口)
def check_info(self):
print("学生姓名:%s,学生年龄:%s %(self.__name,self.__age))
def set_info(self,name,age):
self.__name = name
self.__age = age
1.property就是将方法伪装成数据==》比如说你不会觉得BMI是一个功能,你只会觉得他是一个数据,你就应该用@property
【八】多态
多态可以借助抽象类实现,因为抽象的方法必须重写
1.不同动物,不同叫法,应该调用同一种方法,应该把叫的方法墨守成规的叫同一种方法,这样就不用管他们叫的不一样的函数方法了都把叫的函数叫speak()
eg:列表字符串元组,统计长度的方法都是len(),这就是多态性,减少操作的复杂度
2.需要自己取遵守,与父类中的一样的speak()
3.记住一个类的方法就可以操作所有
【九】反射
1.对象与字符串之间的操作
2.什么时候用,涉及到对象和字符串一定用反射
3.提取关键字
记住:
1.hasattr()==>判断类中有没有字符串对应的数据或函数
2.getattr()==>根据字符串获取对应把你属性值或函数地址<function Student.get at 0x10527a8c8>,函数的运行值
3.setattr()==>根据字符串给的对象设置键值对,新增,键值对,第二个是键,第三个是值
语法:setattr(对象名,要删的函数名或属性名即名称空间字典中的键,要换的函数名或属性名)
注意:有则修改,无则新增
4.delattr()==>根据字符串删除对象对应的键值对==》给键
语法:delattr(对象名,要删的函数名或属性名即名称空间字典中的键)
eg:
class Student(object):
school='清华大学'
def get(self):
pass
s=input("请输入功能")
# s是Student类中的功能或数据
s1=Student()
def index():
print("xxx")
if hasattr(Student,s):
# 获取该数据的值或函数名
# 清华大学
# <function Student.get at 0x10527a8c8>
getattr(Student,s)
print(Student.__dict__)
# 改对应数据名的值,或把函数名换进去了变成另一个函数
# 'get': <function index at 0x0000024315F03E20>
setattr(Student,s,"index")
print(Student.__dict__)
案例1
判断操作时写入还获取,随后根据字符串cmd,获取对象self对应的方法属性会输出
"""Downloading %s...' %file"""
class FtpServer:
def serve_forever(self):
while True:
inp=input('input your cmd>>: ').strip()
cmd,file=inp.split()
if hasattr(self,cmd): # 根据用户输入的cmd,判断对象self有无对应的方法属性
func=getattr(self,cmd) # 根据字符串cmd,获取对象self对应的方法属性
func(file)
def get(self,file):
print('Downloading %s...' %file)
def put(self,file):
print('Uploading %s...' %file)
obj = FtpServer()
obj.serve_forever()
案例2
# 利用面向对象编写系统终端功能
class WinCmd(object):
def ls(self):
print('windows系统正在执行ls命令')
def dir(self):
print('windows系统正在执行dir命令')
def cd(self):
print('windows系统正在执行cd命令')
class LinuxCmd(object):
def ls(self):
print('Linux系统正在执行ls命令')
def dir(self):
print('Linux系统正在执行dir命令')
def cd(self):
print('Linux系统正在执行cd命令')
obj = WinCmd()
obj1 = LinuxCmd()
"""反射提供了一种不需要考虑代码的前提下 操作数据和功能"""
def run(obj):
while True:
cmd = input('请输入您的指令>>>:')
if hasattr(obj, cmd):
func_name = getattr(obj, cmd)
func_name()
else:
print('cmd command not found')
run(obj1)
run(obj)
【十】面向对象双下方法
1.类中__str__是在打印对象显示内容的时候自动触发,且必须返回一个字符串类型
如果类中有写该方法,那么打印对象名打印出来的是str的返回值
2.__del__程序正常运行结束时被动删除,使用del关键字主动删除,可以删除类里的方法,但是不能删除对象和静态方法
3.__getattr__对象查找不存在的名字的时候触发,打印查不到的名字,会返回__getattr__的返回值
4.__setattr__对象在执行添加属性操作时自动触发
5.__call__对象被加括号调用的时候自动触发obj(),类本身就能被call所以不需要写这个方法
6.__enter__对象被执行with(上下文管理)语法开始自动触发
该方法返回什么,as后面就会得到什么
7.__exit__对象被执行with(上下文管理)语法结束自动触发
8.__getattribute__只要对象查找名字,无论名字存不存在都会执行该方法
如果有__getattribute__就不会执行__getattr__
9.__init__实例化对象的时候触发
10.__repr__ : 交互解释器会触发
# 在交互解释器中创建一个Person对象 就会
11.__doc__ : 打印类里面的注释内容的时候会触发
用help()查看
12.__setitem__ : 对象[key]=value 设置值的时候会触发--》中括号设置属性的时候触发
13.__getitem__ : 获取属性的时候会触发 获取方式为 对象[key]
14.__delitem__ : 删除属性的时候会触发 删除方式为 del 对象[key]
双下笔试题
1.让字典具备句点符查找值的功能
# 1.定义一个类继承字典
class MyDict(dict):
def __getattr__(self, item):#自动触发为了获取到字典中的数据
return self.get(item)
def __setattr__(self, key, value):#对象添加属性时操作
#执行obj.pwd = 123一定会调用
self[key] = value#设置了键值对
'''要区别是名称空间的名字还是数据k:v键值对'''
obj = MyDict({'name':'jason','age':18})
# 1.具备句点符取v
# print(obj.name)
# print(obj.age)
# 2.具备句点符设k:v
# obj['gender'] = 'male'
obj.pwd = 123 # 给字典名称空间添加名字 不是数据k:v
print(obj)
2.补全下列代码 使其运行不报错
"""
class Context:
pass
with Context() as ctx:
ctx.do_something()
"""
class Context:
def __enter__(self):
return self
def __exit__(self, exc_type, exc_val, exc_tb):
pass
def do_something(self):
pass
with Context() as ctx:
ctx.do_something()
【十一】元类
1.即产生类的类
2.type其实找的是当前对象的类名称
3.type是所有类默认的类
4.产生类的两种方式
a.class关键字
b.type元类----》返回值也是类
type(类名,父类,类的名称空间)
res = type('C1', (), {})
print(res) # <class '__main__.C1'>
5.学习元类目的--》控制类创建,高度定制类的行为
6.比如:要求类的名字必须首字母大写
思考在哪里编写定制化代码
类的产生过程目前还比较懵 元类里面的__init__方法
对象的产生过程呢 类里面的__init__方法
方法:由已知推未知
7.元类的基本使用
元类不能继承
class MyTypeClass(type):
def __init__(cls, cls_name, cls_bases, cls_dict):
# print(cls, cls_name, cls_bases, cls_dict)
if not cls_name.istitle():
raise Exception("类名的首字母必须大写 你个SD")
super().__init__(cls_name, cls_bases, cls_dict)
class C1(metaclass=MyTypeClass):#可以
school = '清华大学'
class a(metaclass=MyTypeClass):#报错
school = '清华大学'
元类进阶
1.回想__call__方法
对象加括号会自动执行产生该对象的类里面的__call__,并且该方法返回什么对象加括号就会得到什么
推导:类加括号会执行元类的里面的__call__该方法返回什么其实类加括号就会得到什么
"""类里面的__init__方法和元类里面的__call__方法执行的先后顺序"""
class MyTypeClass(type):
def __call__(self, *args, **kwargs):
print('__call__ run')
super().__call__(*args, **kwargs)
class MyClass(metaclass=MyTypeClass):
def __init__(self, name):
print('__init__ run')
self.name = name
obj = MyClass('jason')
# 定制对象的产生过程
class MyTypeClass(type):
def __call__(self, *args, **kwargs):
# print('__call__ run')
# print(args,kwargs)
if args:
raise Exception('必须全部采用关键字参数')
super().__call__(*args, **kwargs)
class MyClass(metaclass=MyTypeClass):
def __init__(self, name):
# print('__init__ run')
self.name = name
"""强制规定:类在实例化产生对象的时候 对象的独有数据必须采用关键字参数"""
# obj1 = MyClass('jason')
obj2 = MyClass(name='jason')
"""
如果你想高度定制类的产生过程
那么编写元类里面的__init__方法
如果你想高度定制对象的产生过程
那么编写元类里面的__call__方法
"""
【十二】双下new
__new__用于产生空对象(类) 骨架
__init__用于实例化对象(类) 血肉
"""
注意:并不是所有的地方都可以直接调用__new__ 该方法过于底层
如果是在元类的__new__里面 可以直接调用
class Meta(type):
def __new__(cls, *args, **kwargs):
obj = type.__new__(cls,*args,**kwargs)
return obj
如果是在元类的__call__里面 需要间接调用
class Mate(type):
def __call__(self, *args, **kwargs):
obj = object.__new__(self) # 创建一个空对象
self.__init__(obj,*args,**kwargs) # 让对象去初始化
return obj
"""
【十三】抽象
# 将某几个具体的生物,根据特征总结成一个类,逐层向上总结
所有继承父类的子类必须重写父类的某些方法,这个父类就叫抽象类
***父类中要继承元类metaclass=abc.ABCMeta,如果要子类必须重写在父类的方法前加 @abc.abstractmethod
import abc
class Animal(metaclass=abc.ABCMeta):
def __init__(self, color, foot, hand):
self.color = color
self.foot = foot
self.hand = hand
def speak(self):
print(f'任何动物都能叫')
# 在子类中必须重写父类的当前方法
@abc.abstractmethod
def walk(self):
...
class BlackBear(Animal):
def __init__(self, color, foot, hand):
super().__init__(color, foot, hand)
# 如果不重写父类的方法就会报错
# Can't instantiate abstract class BlackBear with abstract methods walk
def walk(self):
...