类的补充
一、反射案列
1.1加载配置文件纯大写的配置
首先在settings文件下编写配置信息
# settings AGE = 18 NAME = 'jason' gender = 'male' hobby = 'run'
然后在里一个py文件调用settings中的数据
# 判断settings中的变量名如果是纯大写就使用然后在放到字典中 不是就不使用 new_dict = {} for i in dir(settings): # 将列表中的变量名一个一个取出来 if i.isupper(): # 判断是不是大写 v = getattr(settings, i) # 获取settings中是大写变量的值 new_dict[i] = v # 注册成字典 print(new_dict)
1.2模拟操作cmd端口执行用户的命令
class Mycmd(object): def dir(self): print('获取当前目录下的所有文件名称') def ls(self): print('获取当前路径下的所有文件名称') def ipconfig(self): print('获取当前计算机的网卡信息') obj = Mycmd() while True: cmd = input('请输入执行命令>>>:').strip() # 获取用户的输入指令 if hasattr(obj, cmd): # 判断获取的指令Mycmd中是否有这个属性 cmd_name = getattr(obj, cmd) # 有就获取 cmd_name() # 执行这个属性 else: print('%s 不是内部或外部命令,也不是可运行的程序或批处理文件' % cmd) ''' 请输入执行命令>>>:dir 获取当前目录下的所有文件名称 请输入执行命令>>>:ls 获取当前路径下的所有文件名称 请输入执行命令>>>:ipconfig 获取当前计算机的网卡信息 请输入执行命令>>>:xxx xxx 不是内部或外部命令,也不是可运行的程序或批处理文件 '''
二、面向对象的魔法方法
2.1 什么是魔法方法
魔法方法其实就是定义在类中的双下方法
之所以叫魔法方法是因为这些魔法方法是在达到每个指定条件就会自动触发,无需自己调用
像之前的双下init(__init__)方法就是在实例化对象的时候自动调用的
2.2 各种魔法方法
在类中的魔法方法还是蛮多的, 而且都很重要
2.3 __init__()
class Student(object): def __init__(self): print('实例化对象时自动触发') stu1 = Student() # 实例化对象时自动触发 # 双下init方法是在产生对象时自动触发
2.4 __str__()
class Student(object): def __str__(self): # 对象化执行打印操作的时候就会自动触发该方法 print('对象执行打印操作的时候会自动触发') return '必须返回字符串 返回什么对象打印时候就展示什么字符串' # 必须方法字符串 stu1 = Student() print(stu1) ''' 对象执行打印操作的时候会自动触发 必须返回字符串 返回什么对象打印时候就展示什么字符串
如果不执行打印操作就不会执行该方法
如果不返回字符串就会报错 '''
2.5 __call__()
class Student(object): def __call__(self, *args, **kwargs): # 对象加括号调用时 自动执行该方法 print('__call__方法') print(args, kwargs) stu1 = Student() stu1() # () {} stu1('jason', 18) # ('jason', 18) {} stu1(name='jason', age='18') # () {'name': 'jason', 'age': '18'} # 括号内传什么都会被变长形参接收 ''' __call__方法 () {} __call__方法 ('jason', 18) {} __call__方法 () {'name': 'jason', 'age': '18'} '''
2.6 __getattr__()
class Student(object): ''' 就是对象在调类中没有的属性的时候就会自动触发该属性 形参item 就是用户想要查找的不存在的属性名 该方法返回什么 对象获取不存在的属性名的时候得到什么 ''' def __getattr__(self, item): print('__getattr__方法') print(item) return '您想要查找的属性:%s没有找到' % item stu1 = Student() print(stu1.name) ''' __getattr__方法 name 您想要查找的属性:name没有找到 '''
2.7 __setattr__()
class Student(object): ''' 该方法就是在对象操作属性名的时候自动触发 因为名称空间像是一个字典所以 key就是属性名 value就是数据值 然后把这个属性名和数据值给放到名称空间当中 ''' def __setattr__(self, key, value): print('__setattr__方法') print(key) print(value) super().__setattr__(key,value) stu1 = Student() stu1.name = 'jason' ''' __setattr__方法 name jason '''
2.8 __del__()
class Student(object): def __init__(self, name, age): self.name = name self.age = age ''' __del__方法是对象在被(主动、被动)删除的时候 自动触发 主动就是我们自己使用del方法删除属性的时候 被动就是我们在运行完py文件的时候也会清空内存 然后就会触发 ''' def __del__(self): print('__del__方法') stu1 = Student('jason', 18) # __del__方法 # del stu1.name # __del__方法
2.9 __getattribute__
class Student(object): def __init__(self, name): self.name = name ''' __getattribute__方法就是在对象在获取属性时自动触发 无论这个属性存不存在 item 就是用户想要获取属性的属性名 当类中有__getattr__又有__getattribute__ 方法时只会走后者 ''' def __getattribute__(self, item): print('__getattribute__方法') print(item) stu1 = Student('jason') stu1.name # 现在类中只有name这个属性 ''' __getattribute__方法 name ''' stu1.age ''' __getattribute__方法 age ''' class Student(object): def __init__(self, name): self.name = name def __getattribute__(self, item): print('__getattribute__') stu1 = Student('jason') print(stu1.name) ''' 现在在类中是有self.name = name 的但是现在调用那么却是None 那是因为 这个__getattribute__是我们自己写的功能 我们虽然继承了object 但是根据名字查找顺序是用不到object里的__getattribute__的方法的 而我们__getattribute__方法只有一个打印功能 所以是没有获取用户想要的属性的 所以我们只要用上派生方法即可 __getattribute__ None ''' class Student(object): def __init__(self, name): self.name = name def __getattribute__(self, item): print('__getattribute__') return super().__getattribute__(item) stu1 = Student('jason') print(stu1.name) ''' 使用派生就可以拿到想要拿到的属性了 __getattribute__ jason '''
2.10 __enter__()
class Student(object): def __enter__(self): ''' 该方法就是在对象在被with语法调用的时候自动触发 该方法返回什么 as后面的变量名就会接收到什么 ''' print('__enter__') return 1 def __exit__(self, exc_type, exc_val, exc_tb): ''' 该方法就是在对象在被with语法执行并子代码结束的时候自动调用 ''' print('__exit__') stu1 = Student() with stu1 as f: print(f) ''' __enter__ 1 __exit__ '''
三、魔法方法笔试题
补全代码 是代码不报错
class Context: pass with Context() as f: f.do_something() # # 如果就这样写的话代码是肯定会报错的 # 因为对象是被with语法调用所以我们可以在类中加入__enter__ 、__exit()方法 class Context: def __enter__(self): return self # 因为该方法返回什么as后面的变量名就会接收什么 所以我们直接返回一个对象本身 def __exit__(self, exc_type, exc_val, exc_tb): pass with Context() as f: f.do_something() # 这个时候就会报类中没有do_something方法我们直接在类中定义该方法即可 class Context: def __enter__(self): return self # 因为该方法返回什么as后面的变量名就会接收什么 所以我们直接返回一个对象本身 def __exit__(self, exc_type, exc_val, exc_tb): pass def do_something(self): # pass with Context() as f: f.do_something()
四、元类简介
''' 我们之前在查看数据类型的时候都是在使用type方法 ''' l = [] d = {} print(type(l), type(d)) # <class 'list'> <class 'dict'> ''' 但是我们在学了面向对象之后我们可以发现 其实我们在查看数据类型的时候其实是在查看数据所属的类 ''' class MyClass(object): pass obj = MyClass() print(type(obj)) # <class '__main__.MyClass'> # obj 就是属于MyClass类的数据 # 我们在定义一个数据类型的时候 其实本质还是通过各个类来产生对象 class str(): pass h = 'jaosn' # 其实等同于 str('jason') # 所以我们可以把type用于查看产生当前对象的类是谁 # 然后我们现在想看一下我们定义的类又是谁产生的呢 class MyClass(): pass obj = MyClass() print(type(obj)) # <class '__main__.MyClass'> print(type(MyClass)) # <class 'type'> ''' 然后我们就会发现其实我们定义的类就是通过type产生的
然后我们把产生类的类称之为元类 '''
4.1 产生类的两种方式
# 第一种: class关键字 class MyClass(object): pass # 第一种是最普遍的方法而且还是最简单的一种方法 # 第二种:使用元类type方法 type(类名, 类的父类, 类的名称空间) ''' 学习元类其实就是知道了类的产生过程 这样我们就可以在类的产生过程中高度定制化类的行为 eg: 类名必须首字母大写 上述方法必须使用元类来控制类的产生过程 在过程中校验 '''
五、元类的基本使用
# class MyMetaClass(type): # pass
# 只有继承了type的类才能被称之为元类 ''' 因为我们的自己写的类继承了type所以也被称之为元类 如果想让产生类的不是使用type产生的 而是想要用我们自己写的类MyMetaClass产生 那么必须使用metaclass关键字 ''' class MyClass(metaclass=MyMetaClass):pass ''' 而类中的__init__是实例化对象的 元类的__init__就是用来产生类的 ''' class MyClass(type): def __init__(self, what, bases=None, dict=None): print('what', what) # what就是类的名字 print('bases', bases) # bases就是类的父类 print('dict', dict) # dict就是类的名称空间 if not what.istitle(): raise Exception ('首字母必须大写') # class student(metaclass=MyClass): # 如果定义类的时候类名首字母不是大写就会报错 # pass class Student(metaclass=MyClass): # 首字母大写就不会报错 pass # 所以我们就可以使用元类来定制我们想要的类 ''' what Student bases () dict {'__module__': '__main__', '__qualname__': 'Student'} '''
六、元类进阶
# 元类不仅可以控制类的产生还可以控制对象的产生 ''' 对象加括号是执行产生对象的类中的__call__方法 而对象加括号就是执行该类的元类中的__call__方法 ''' # 我们现在要让对象使用关键字参数传参 class MyMetaClass(type): def __call__(self, *args, **kwargs): print('__call__') if args: # 因为args是接收多余的位置参数 所以单args有值时说明用户是用位置参数传值了 raise Exception('必须要用关键字传参') super().__call__(*args, **kwargs) class MyClass(metaclass=MyMetaClass): def __init__(self, name, age): self.name = name self.age = age # obj = MyClass('jason', 18) # 如果使用位置参数传参就会报错 obj = MyClass(name='jason', age=18) # __call__ ''' 而类加括号就会自动执行__call__方法 所以会先执行MyMetaClass中的__call__方法 就会先判断传入的值是否是关键字参数 ''' ''' 总结: 如果我们想要高度定制对象的产生过程 可以操作元类中的__call__方法 如果我们先要高度定制类的产生过程
七、__new__方法
''' 我们在用类产生对象的时候的过程: 1.产生一个空对象 2.自动触发__init__方法实例化对象 3.返回实例化好的对象 ''' # 那么怎么产生一个空对象呢? # __new__方法就是产生一个空对象 # __init__方法就是给空对象添加属性的
八、作业
# d = { # 'name': 'jason', # 'age': 18 # } # 我们在使用字典添加和修改值的时候一般都是加中括号修改或添加 # d['name'] = 'kevin' # 存在就修改 # d['gender'] = 'male' # 不存在就添加 # print(d) # {'name': 'kevin', 'age': 18, 'gender': 'male'} # 为我现在想要通过点的形式 添加或修改 # d.name = value 添加 # d.name = value 修改 d = { 'name': 'tony', 'age': 18 } class MyClass(dict): def __getattr__(self, item): return self.get(item) def __setattr__(self, key, value): self[key] = value return value obj = MyClass(d) obj.name = 'jason' print(obj) # {'name': 'jason', 'age': 18} obj.gender = 'male' print(obj) # {'name': 'jason', 'age': 18, 'gender': 'male'}