您现在的位置是:网站首页> 编程资料编程资料

Python面向对象编程(三)_python_

2023-05-26 424人已围观

简介 Python面向对象编程(三)_python_

Python面向对象编程(一)

Python面向对象编程(二)

Python面向对象编程(三)

一、isinstance和issubclass

  • type():不会认为子类实例是一种父类类型;
  • isinstance():认为子类实例是一种父类类型。
  • issubclass():判断是否为其子类。
class Foo(object): pass class Bar(Foo): pass print(type(Foo()) == Foo) # True print(type(Bar()) == Foo) # False # isinstance参数为对象和类 print(isinstance(Bar(), Foo)) # True print(issubclass(Bar, Foo)) # True print(issubclass(Foo, object)) # True

二、反射(hasattr和getattr和setattr和delattr)

1、反射在类中的使用

反射就是通过字符串来操作类或者对象的属性。反射本质就是在使用内置函数,其中反射有以下四个内置函数:

  • hasattr:判断一个方法是否存在与这个类中
  • getattr:根据字符串去获取obj对象里的对应的方法的内存地址,加"()"括号即可执行
  • setattr:通过setattr将外部的一个函数绑定到实例中
  • delattr:删除一个实例或者类中的方法
class People: country = 'China' def __init__(self, name): self.name = name def eat(self): print('%s is eating' % self.name) peo1 = People('nick') print(hasattr(peo1, 'eat')) # peo1.eat # True print(getattr(peo1, 'eat')) # peo1.eat # > print(getattr(peo1, 'xxxxx', None)) # None setattr(peo1, 'age', 18) # peo1.age=18 print(peo1.age) # 18 print(peo1.__dict__) # {'name': 'egon', 'age': 18} delattr(peo1, 'name') # del peo1.name print(peo1.__dict__) # {'age': 18}

2、反射在模块中的使用

动态导入一个模块__import__,并且动态输入函数名然后执行相应功能。

注意:getattr,hasattr,setattr,delattr对模块的修改都在内存中进行,并不会影响文件中真实内容。

# dynamic.py imp = input("请输入模块:") commons = __import__(imp) # 等价于import imp # commons = __import__(imp, fromlist=True) # 模块名可能不是在本级目录中存放着,改用这种方式就能导入成功 inp_func = input("请输入要执行的函数:") f = getattr(commons, inp_func, None) # 作用:从导入模块中找到你需要调用的函数inp_func,然后返回一个该函数的引用.没有找到就烦会None f() # 执行该函数 r = hasattr(commons, 'age') # 判断某个函数或者变量是否存在 print(r) setattr(commons, 'age', 18) # 给commons模块增加一个全局变量age = 18,创建成功返回none setattr(commons, 'age', lambda a: a + 1) # 给模块添加一个函数 delattr(commons, 'age') # 删除模块中某个变量或者函数

3、实例:基于反射机制模拟web框架路由

需求:比如我们输入 ,返回f1的结果。

# 动态导入模块,并执行其中函数 url = input("url: ") target_host,target_module, target_func = url.split('/') m = __import__('aaa.' + target_module, fromlist=True) inp = url.split("/")[-1] # 分割url,并取出url最后一个字符串 if hasattr(m, inp): # 判断在commons模块中是否存在inp这个字符串 inp= getattr(m, inp) # 获取inp的引用 inp() # 执行 else: print("404")

三、__getattr__、__setattr__和__delattr__和__getattribute__事件

  • __getattr__:只有在使用点调用属性且属性不存在的时候才会触发。比较有用
  • __delattr__:删除属性的时候会触发
  • __setattr__:添加/修改属性会触发它的执行
    当你自己写__getattr__、__delattr__、__setattr__方法,系统会调用你写的方法,如果没写,系统调用默认
class Foo: x = 1 def __init__(self, y): self.y = y def __getattr__(self, item): print('----> from getattr:你找的属性不存在') def __setattr__(self, key, value): print('----> from setattr') # self.key = value # 这就无限递归了,你好好想想 # self.__dict__[key] = value # 应该使用它 def __delattr__(self, item): print('----> from delattr') # del self.item # 无限递归了 self.__dict__.pop(item) f1 = Foo(10) # ----> from setattr print(f1.__dict__ ) # 因为你重写了__setattr__,凡是赋值操作都会触发它的运行,你啥都没写,就是根本没赋值。除非你直接操作属性字典,否则永远无法赋值 # {} f1.z = 3 # ----> from setattr print(f1.__dict__) # {} f1.__dict__['a'] = 3 # 我们可以直接修改属性字典,来完成添加/修改属性的操作(不会触发__setattr__) del f1.a # ----> from delattr print(f1.__dict__) # {}

__getattribute__

查找属性无论是否存在,都会执行。

class Foo: def __init__(self, x): self.x = x def __getattribute__(self, item): print('不管是否存在,我都会执行') f1 = Foo(10) f1.x # 不管是否存在,我都会执行 f1.xxxxxx # 不管是否存在,我都会执行

当__getattribute__与__getattr__同时存在,只会执行__getattrbute__,除非__getattribute__在执行过程中抛出异常AttributeError

class Foo: def __init__(self, x): self.x = x def __getattr__(self, item): print('执行的是我') # return self.__dict__[item] def __getattribute__(self, item): print('不管是否存在,我都会执行') raise AttributeError('哈哈') f1 = Foo(10) f1.x # 不管是否存在,我都会执行 # 执行的是我 f1.xxxxxx # 不管是否存在,我都会执行 # 执行的是我

四、__setitem__和__getitem和__delitem__

  • __setitem__:中括号赋值时触发
  • __getitem__:中括号取值时触发
  • __delitem__:中括号删除时触发
  • __delattr__:.删除时触发
class Foo: def __init__(self, name): self.name = name def __getitem__(self, item): print('getitem执行', self.__dict__[item]) def __setitem__(self, key, value): print('setitem执行') self.__dict__[key] = value def __delitem__(self, key): print('del obj[key]时,delitem执行') self.__dict__.pop(key) def __delattr__(self, item): print('del obj.key时,delattr执行') self.__dict__.pop(item) f1 = Foo('sb') f1['age'] = 18 # setitem执行 f1['age1'] = 19 # setitem执行 f1['age'] # getitem执行 18 f1['name'] = 'tank' # setitem执行 del f1.age1 # del obj.key时,delattr执行 del f1['age'] # del obj[key]时,delitem执行 print(f1.__dict__) # {'name': 'tank'}

五、__format__:自定制格式化字符串

date_dic = { 'ymd': '{0.year}:{0.month}:{0.day}', 'dmy': '{0.day}/{0.month}/{0.year}', 'mdy': '{0.month}-{0.day}-{0.year}', } class Date: def __init__(self, year, month, day): self.year = year self.month = month self.day = day def __format__(self, format_spec): # 默认打印ymd的{0.year}:{0.month}:{0.day}格式 if not format_spec or format_spec not in date_dic: format_spec = 'ymd' fmt = date_dic[format_spec] return fmt.format(self) d1 = Date(2016, 12, 29) print(format(d1)) # 2016:12:29 print('{:mdy}'.format(d1)) # 12-29-2016

六、__del__:析构方法

会在对象被删除之前自动触发

class People: def __init__(self, name, age): self.name = name self.age = age self.f = open('test.txt', 'w', encoding='utf-8') def __del__(self): print('run======>') # 做回收系统资源相关的事情 self.f.close() obj = People('egon', 18) del obj # del obj会间接删除f的内存占用,但是还需要自定制__del__删除文件的系统占用 # run=-====>

七、__slots__

使用点来访问属性本质就是在访问类或者对象的__dict__属性字典(类的字典是共享的,而每个实例的是独立的)。

__slots__是一个类变量,变量值可以是列表,元祖,或者可迭代对象,也可以是一个字符串(意味着所有实例只有一个数据属性)

字典会占用大量内存,如果你有一个属性很少的类,但是有很多实例,为了节省内存可以使用__slots__取代实例的__dict__。

class Foo: __slots__ = 'x' f1 = Foo() f1.x = 1 f1.y = 2 # 报错 print(f1.__slots__ ) # f1不再有__dict__

当你定义__slots__后,__slots__就会为实例使用一种更加紧凑的内部表示。使用__slots__后不能再给实例添加新的属性了,只能使用在__slots__中定义的那些属性名。

注意:__slots__的很多特性都依赖于普通的基于字典的实现。另外,定义了__slots__后的类不再支持一些普通类特性了,比如多继承。

大多数情况下,你应该只在那些经常被使用到的用作数据结构的类上定义__slots__比如在程序中需要创建某个类的几百万个实例对象 。

class Bar: __slots__ = ['x', 'y'] n = Bar() n.x, n.y = 1, 2 n.z = 3 # 报错

八、__doc__:返回类的注释信息

class Foo: '我是描述信息' pass print(Foo.__doc__) # 我是描述信息

-六神源码网