面向对象鱼龙混杂
Posted panshao51km-cn
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了面向对象鱼龙混杂相关的知识,希望对你有一定的参考价值。
一.issubclass()
判断第一个类是不是第二个类的子类, 返回True或者是false
class Foo: pass class Bar(Foo): pass print(issubclass(Bar, Foo)) # True
二. isinstance()
判断第一个参数是不是第二个参数的对象,返回True或者false
class Foo: pass class Bar: pass f = Foo() print(isinstance(f, Foo)) # True
三. 反射
主要是指程序可以访问, 检测和修改它本身的状态或者行为的一种能力,也就是自省的意思,程序在运行的过程中,他是可以修改,检测,访问的自己的属性,修改对象的方式可以用点来修改, 函数跟模块就不能用哪个点来进行修改,在Python设计之初,就是把一切看做对象,这里就用到反射.
用户输入一段字符串,执行该字符串对应的方法 class Foo: def run(self): print(run) def speak(self): print(‘speak‘) p = Foo() cmd = input(‘请输入命令:‘) if hasattr(p, cmd): # 判断某个属性是不是在这个对象中 run = getattr(p, cmd) run() else: print(‘该命令不存在‘) key = input("请输入key:") value = input("请输入value:") setattr(p, key, value) # 将key和value全都放置在p中 print(p.age) # 假如我们key设置为age,那么可以通过key就可以取出value值 # 动态的往对象中放方法 def test(a) print(a) print(p.__dict__) # setattr(p, ‘test‘, test) print(p.__dict__) # ‘test‘:<function test at 0x0000022B6ADFEA0> p.test(0) # hasattr() 判断一个属性是不是在对象中 # getattr() 通过字符串获取属性和方法 # setattr() 通过字符串来设置属性和方法 # delattr() 通过字符串来删除属性和方法
有了反射的setattr方法,以后相加某个方法就可以随便的加了
# 动态的删除属性 # 原始的删除方法 p.name = ‘lqz print(p.__dict__) # ‘name‘: ‘panshao‘ del p.name # 直接拿属性 print(p.__dict__) # # 动态删除p中属性为变量a的属性 a = input(‘请输入要删除的属性:‘) delattr(p, a)
四. 内置方法
__str__
class Foo: pass f = Foo() print(f)
如果不重写__str__,print打印会打印出内存地址,如果重写了会打印出你想要的
class Foo: def __str__(self): return ‘xxx‘ f = Foo() print(f) # 当print对象的时候就去调用里面的函数,相当于print(f.__str__())
__repr__跟__str__相似, 在交互式的命令下直接写变量名,会执行__repr__,不用写print
__setattr__, __delattr__, __getattr__(重要)
class Foo: def __init__(self, name) self.name = name f = Foo(‘panshao‘) f.age # 没有age 这个属性,就会出现报错 这时候就可以用__getattr__ # __getattr__: 点拦截方法, 如果去对象中取属性, 取不到,会进入__getattr__ class Foo: def __init__(self, name) self.name = name def __getattr__(self): print(‘的点点滴滴‘) f = Foo() f.age # 打印出的点点滴滴
class Foo: def __init__(self, name) self.name = name f = Foo(‘panshao‘) # 赋值时已经触发了__setattr__运行 print(f.name)
def __delattr__(self, item): #self传的是调用者自己 itme传的是调用错误的函数信息 print(‘----> from delattr‘) # del self.item #无限递归了 #self.__dict__.pop(item) #同理直接操作字典
#__delattr__删除属性的时候会触发 #del f1.x #只要是删除操作就会调用__delattr__函数执行,值不存在也不会报错 # f1.__dict__[‘a‘]=3#我们可以直接修改属性字典,来完成添加/修改属性的操作 # del f1.a # print(f1.__dict__)
看一个例子:
#写一个类继承字典,让它可以 . 取值,可以中括号取值 class Mydict(dict): def __init__(self,**kwargs): super().__init__(**kwargs) def __getattr__(self, item): return self[item] def __setattr__(self, key, value): self[key]=value di=Mydict(name=‘lqz‘,age=18) print(di[‘name‘]) print(di.name) di.sex=‘male‘ di[‘sex‘]=‘male‘ print(di[‘name‘]) print(di.name) di.sex=19 print(di.sex) di[‘sex‘]=‘male‘ print(di.sex)
__call__ 对象加括号会调用他
class Foo: pass f = Foo() f() # 报错 class Foo: def __call__(self) print(‘xxx‘) f = Foo() f() # xxxx
五. 元类
产生类的类就叫元类, type类是产生所有类的元类,type能够实例化产生object, type也能继承object,type是内置的一个元类,所有的类都是由type实例化得到
class关键字底层实现原理:
# class 类名 : 就会把类构造出来 # 实际上是: 元类实例化产生类这个对象 # 类实例化产生对象,一定是: 类名() # Person类是由type实例化产生,传一堆参数 # type(object_or_name, base, dict) object_or_name: 类的名字, 是个字符串 # base: 是他的所有父类,基类 # dict: 名称空间, 是个字典,有方法,有属性 class Person: # Person 是类也是对象 def __init__(self, name): self.name = name def score(self): print(‘分数是100分‘) p = Person(‘panshao‘)
class的底层就是调用type来实例化产生类(对象), 自定义元类必须继承type, 写一个类继承type, 这种类就叫做元类
通过元类控制类的产生:
class Mymeta(type): pass # metaclass = Mymeta 指定这个类生成的时候,用自己写的Mymeta这个元类 class Person(metaclass=Mymeta): # Person 是类也是对象 def __init__(self, name): self.name = name def score(self): print(‘分数是100分‘) p = Person(‘panshao‘) # 自定义元类:来控制类的产生,可以控制类名,可以控制类的关系(继承父类,控制类的名称空间)
通过元类来控制类的产生过程:
class Mymeta(type): def __call__(self, *args, **kwargs): # self是Person这个类 # 实例化产生一个Person类的对象,借助__new__来产生,需要把类传过去,才能产生对象 # obj 是Person类的对象,只不过是空的 obj = object.__new__(self) # obj是空的 # 调用__init__方法实现初始化 self.__new__(obj, *args, **kwargs) # 类来调用__init__方法,就是个普通函数,有几个参数就传几个参数 # obj.__new__(*args, **kwargs) # 对象来调用__init__方法,对象的绑定方法,会把自身传过来 class Person(metaclass=Mymeta): def __init__(self, name): self.name = name def score(self): print(‘分数是100分‘) def __call__(self): print("11111") p = Person(‘panshao‘) # 自动触发init的执行 Person加括号就是调用元类的call方法 # 先触发元类的__call__ p() # p()是调用person的call
__new__:
class Person: def __init__(self, name, age) print("__init__") self.name = name self.age = age def __new__(cls, *args, **kwargs): # print("__new__") # 生成一个空对象, return出去,在元类的__call__里调用了__init__来完成初始化 return object.__new__(cls) p = Person(‘panshao‘, 18) # 打印出__new__ 奇怪为什么没有触发__init__, print(p) # none p = Person(‘panshao‘, 18) # 打印出__new__ 奇怪为什么没有触发__init__, p = Person(‘panshao‘, 18)先调用__call__方法,__call__再调用自己的self的__new__,就会得到一个空对象,假如我在__new__的方法中return一个1,那么这个空对象就是1,return后面的空对象一旦有值才会调用__init__
__new__和__init__的区别: __new__创建对象,必须有返回值, __init__初始化对象,就是给对象穿衣服
object.__new__(Person): 生成person类的对象, 空的
type.__new__(cls, name, bases, dic): 生成class这个类对象, 里面有东西
type类: __call__: 1. 调用Mymeta的__new__,得到类对象, 2. 调用Mymeta的__init__,完成对类对象的初始化
自定义的Mymeta元类: __call__ 1.调用了Person类的__new__,得到对象, 2. 调用Person类的__init__, 完成对象的初始化
六. 单例模式(23中设计模式的一种)
整个过程中只有一个实例, 所有生成的实例都指向同一块内存空间
class Person: def __init__(self, name , age) self.name = name self.age = age p1 = Person(‘panshao‘, 18) p2 = Person(‘bgon‘,19) print(p1) print(p2)
像上面这种情况就不是单例模式,他实例化出两个对象,单例模式是每次实例化出现的是同一个对象
实现单例的第一种方式:通过类的绑定方法
port = 3306 host = ‘127.0.0.1‘ class Sql: _instance = None def __init__(self, port, host): self.port = port self.host = host @classmethod def get_sigleton(cls): if not cls._instance: cls._instance = cls(‘port‘, ‘host‘) return cls._instance s1 = Sql.get_sigleton() s2 = Sql.get_sigleton() # 这样调用的话会实例化出不同的结果,我们现在做的就是要求每次拿到的实例化的结果都是一样的,我们可以在类的名称空间中加一个属性_instance
拿到的结果都是一样的,其实就是我每次要值得话,就去名称空间里面去取值就行
第二种方法: 通过装饰器
第二种方法:通过装饰器 当用户输入端口和地址,实例化产生新对象 当用户不输入端口和地址,每次拿到的对象,都是同一个 def get_sigoleton(cls): #cls就是Sql这个类 import settings _instance=cls(settings.PORT, settings.HOST) _instance=Sql(settings.PORT, settings.HOST) def wrapper(*args,**kwargs): if len(args)!=0 or len(kwargs)!=0: #表示传了参数,生成新对象 res=cls(*args,**kwargs) return res else: return _instance return wrapper def get_sigoleton(cls): _instance=None def wrapper(*args,**kwargs): if len(args)!=0 or len(kwargs)!=0: #表示传了参数,生成新对象 res=cls(*args,**kwargs) return res else: import settings nonlocal _instance if not _instance: _instance=cls(settings.PORT, settings.HOST) return _instance return wrapper @get_sigoleton #会把下面的Sql当中参数传入,相当于:Sql=get_sigoleton(Sql) class Sql(): def __init__(self,port,host): self.port=port self.host=host Sql=get_sigoleton(Sql) s1=Sql() s2=Sql() s3=Sql(‘33306‘,‘192.168.1.1‘) s4=Sql(‘33306‘,‘192.168.1.1‘) print(s1) print(s2) print(s3)
第三种: 通过元类
#第三种,通过元类 #当用户输入端口和地址,实例化产生新对象 #当用户不输入端口和地址,每次拿到的对象,都是同一个 # class Mymeta(type): # def __init__(self,name,bases,dic): # #self 是Sql类 # import settings # #把实例化好的对象,放到了类的名称空间 # self._instance=self(settings.PORT, settings.HOST) # def __call__(self, *args, **kwargs): # #self是谁?是Sql类 # if len(args)!=0 or len(kwargs)!=0: # obj=object.__new__(self) # obj.__init__(*args, **kwargs) # return obj # else: # return self._instance # # class Sql(metaclass=Mymeta): #相当于 Sql=Mymeta(name,bases,dic) 这个会调用 Mymeta的__init__ 在里面已经向类的名称空间放了一个对象 # def __init__(self,port,host): # self.port=port # self.host=host # # print(Sql.__dict__) # s1=Sql() # #调用元类的__call__ # s2=Sql() # s3=Sql(‘33306‘,‘192.168.1.1‘) # print(s1) # print(s2) # print(s3)
以上是关于面向对象鱼龙混杂的主要内容,如果未能解决你的问题,请参考以下文章