Python之旅(八)面向对象02

isinstance(obj,cls)和issubclass(sub,super)

 isinstance(obj,cls)检查是否obj是否是类 cls 的对象
 issubclass(sub, super)检查sub类是否是 super 类的派生类(子类)

class Animal():
    pass

class Dog(Animal):
    pass

d1 = Dog()
# 检查d1是不是Dog的一个对象
print(isinstance(d1, Dog))

print(issubclass(Dog, Animal))
print(issubclass(Animal, object))

# 运行结果
# True
# True
# True

反射

概念:反射的概念是由Smith在1982年首次提出的,主要是指程序可以访问、检测和修改它本身状态或行为的一种能力(自省)

四个可以实现自省的函数:

这四个方法适用于对象和类(一切皆对象,类也是一种对象)

 1 class Dog():
 2     type = '犬科'
 3 
 4     def __init__(self, name, gender):
 5         self.name = name
 6         self.gender =gender
 7 
 8     def info(self):
 9         print('name is {}, gender is {}'.format(name, gender)) 10 11 d1 = Dog('小黄', '母') 12 13 # 检测是否可以使用某属性(数据属性、方法属性) 14 print(hasattr(d1, 'type')) 15 16 # 获取属性对应的属性值 17 print(getattr(d1, 'type')) 18 #print(getattr(d1, 'age')) #找不到 d1.age 报错 19 print(getattr(d1, 'age', '没有找到age')) #可以设置返回值,找不不报错 20 21 # 设置属性 22 setattr(d1, 'type', '大黄') 23 print(getattr(d1, 'type')) 24 setattr(d1, 'add', lambda x:x+1) #也可以设置函数属性 25 print(d1.add) #<function <lambda> at 0x02EE07C8> 26 print(d1.add(3)) #4 27 28 29 # 删除属性 30 delattr(d1, 'type') 31 #delattr(d1, 'abc') #不存在,报错 32 33 print(d1.__dict__)

动态导入模块

 1 # from mod import a
 2 
 3 # import mod.a
 4 
 5 # 使用__import__导入,拿到的是最顶级的模块
 6 m = __import__('mod.a')
 7 print(m) #<module 'mod' (namespace)>
 8 m.a.p()
 9 
10 # 使用importlib模块处理模块的导入操作时,拿到的时目标模块
11 import  importlib
12 m = importlib.import_module('mod.a')
13 print(m) #<module 'mod.a' from 'E:\\PycharmProjects\\untitled\\day26\\mod\\a.py'>
14 m.p()

__getattr__ __setattr__ __delattr__

# __getattr__ __setattr__ __delattr__是类的内置函数,dir()查看
class Dog():
    type = 'animal'
    def __init__(self, name):
        self.name = name

    # 实例使用点.调用不存在的属性时触发
    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") d1 = Dog('小黄') print(d1.__dict__) #因为你重写了__setattr__,凡是赋值操作都会触发它的运行,你啥都没写,就是根本没赋值,除非你直接操作属性字典,否则永远无法赋值  d1.type #可以访问到,没有触发__getattr__ d1.namessssss d1.name = "大黄" d1.__dict__['age'] = 18 #我们可以直接修改属性字典,来完成添加/修改属性的操作 print(d1.__dict__) del d1.age

执行结果:

1 from setattr 
2 {}
3 from getattr 没有找到该属性
4 from setattr 
5 {'age': 18}
6 from delattr

包装和授权

包装:python为我们提供了标准数据类型,以及丰富的内置方法,其实在很多场景下我们都需要基于标准数据类型来定制我们自己的数据类型,新增/改写方法,这就是包装。也就是对基本数据类型进行二次加工

通过继承和派生实现包装

class List(list):
    def append(self, object):
        '派生自己的appdend:只允许追加int类型'
        if type(object) is int:
            super().append(object)
        else:
            print('只支持append整型int')

    @property
    def middle(self):
        """新增一个方法,用于获取列表的中间值"""
        i = int(len(self)/2)
        return self[i]

x = [1, 2, 3, 4]
print(type(x))

lx = List(x)
print(type(lx))

lx.append(5)
print(lx)
lx.append('a')

print(lx.middle)

# 运行结果
# <class 'list'>
# <class '__main__.List'>
# [1, 2, 3, 4, 5]
# 只支持append整型int
# 3

通过授权实现包装

授权:通过授权的方式实现包装

授权的过程,即是所有更新的功能都是由新类的某部分来处理,但已存在的功能就授权给对象的默认属性。

实现授权的关键点就是覆盖__getattr__方法

 1 class List():
 2     def __init__(self, seq):
 3         self.seq = seq
 4 
 5     def append(self, obj):
 6         if type(obj) is int:
 7             self.l.append(obj)
 8         else:
 9             print("只能append整型int")
10 
11     def __getattr__(self, item):
12         return getattr(self.seq, item)
13 
14     def __str__(self):
15         return str(self.seq)
16 
17 i = List([1, 2])
18 i.append("a")
19 print(i)

__getattribute__

 1 class Dog():
 2     def __init__(self, name):
 3         self.name = name
 4 
 5     def __getattr__(self, item): #当getattrbute抛出AttributeError异常时才执行
 6         print("这个玩意不存在from getattr")
 7 
 8     def __getattribute__(self, item): #实例寻找变量时都会触发
 9         print('无论是否存在都执行 from getattrbute')
10         raise AttributeError('这个玩意不存在,我要抛出一个异常') 11 12 d1 = Dog('小黄') #触发__getattribute__,但是这里没有成功赋值 13 d1.name 14 d1.age

__setitem__,__getitem,__delitem__

在使用实例字典进行操作时会触发 __setitem__   __getitem__  __delitem__

在使用实例加点对变量进行操作时会触发 __setattr__  __getattr__  __delattr__

 1 class Dog():
 2     def __getitem__(self, item):
 3         print('from getitem')
 4         # return self.__dict__[item] #返回操作
 5 
 6     def __setitem__(self, key, value):
 7         print('from setitem')
 8         #self.__dict__[key] = value 赋值操作
 9     def __delitem__(self, key): 10 print('from delitem') 11 #self.__dict__.pop(key) #删除操作 12 13 d1= Dog() 14 d1.age = 11 #使用实例名加点 . 的方式会触发getattr,不会触发getitem 15 print(d1.__dict__) 16 17 del d1['age'] #使用字典的方式,会触发delitem 18 d1['name'] = '小黄' 19 print(d1['name'])

_str__,__repr__,__format__

常见的对象显示字符串的格式:

l = [1, 2, 3]
print(l)

class Dog():
    pass

d1 = Dog()
print(d1)

f = open('test.txt', 'w')
print(f)

# 运行结果如下
# [1, 2, 3]
# <__main__.Dog object at 0x00CA9C30>
# <_io.TextIOWrapper name='test.txt' mode='w' encoding='cp936'>
View Code

_str__,__repr__,__format__必须有返回值,且返回值只能是字符串,否则抛异常

  print()和str()都会触发obj.__str__()

  repr()或者交互解释器中都会触发obj.__repr__()

  如果str没有定义,则使用repr来代替输出

str和repr

class Dog():
    def __init__(self, name):
        self.name = name

    def __str__(self):
        print('from __str__')
        return ('这条狗的名字是%s' %self.name)

    def __repr__(self):
        return ('from repr: %s' %self.name)

d1 = Dog('小黄')
print(d1) #print()和str()都会触发obj.__str__()
str(d1)

print(repr(d1)) #repr()或者交互解释器中都会触发obj.__repr__()

# str repr的必须有返回值,返回值必须是字符串,否则跑异常
# 如果str没有定义,则使用repr来代替输出

# 运行结果
# from __str__
# 这条狗的名字是小黄
# from __str__
# from repr: 小黄

 format

class Dog():
    def __init__(self, name, age, gender):
        self.name = name
        self.age = age
        self.gender =gender
    def __format__(self, format_spec):
        return 'from format'


d1 = Dog('小黄', 18, '')
print(format(d1)) #format()触发__format__()
print('{}'.format(d1)) #触发__format()

# 运行结果
# from format
# from format
format_dict = {
    'nag': 'name:{0.name}, age:{0.age}, gender:{0.gender}',
    'agn': 'age:{0.age}, gender:{0.gender}, name:{0.name}',
    'gna': 'gender:{0.gender}, name:{0.name}, age:{0.age}',
}

class Dog():
    def __init__(self, name, age, gender):
        self.name = name
        self.age = age
        self.gender =gender
    def __format__(self, format_spec):
        if not format_spec or format_spec not in format_dict:
            format_spec = 'nag'
        i = format_dict[format_spec]
        return i.format(self)

d1 = Dog('小黄', 18, '')

print(format(d1, 'agn'))
print(format(d1, 'dasjgi sd h'))

# 运行结果
# age:18, gender:公, name:小黄
# name:小黄, age:18, gender:公

__slots__

__slots__取代了实例的__dict__字典

当一个类中变量很少,但是这个类的实例很多时,每一个实例都有一个属性字典会占用大量内存,此时__slots__可以帮助节省内存,但是属性字典的相关操作就限制了

'''
1.__slots__是什么:是一个类变量,变量值可以是列表,元祖,或者可迭代对象,也可以是一个字符串(意味着所有实例只有一个数据属性)
2.引子:使用点来访问属性本质就是在访问类或者对象的__dict__属性字典(类的字典是共享的,而每个实例的是独立的)
3.为何使用__slots__:字典会占用大量内存,如果你有一个属性很少的类,但是有很多实例,为了节省内存可以使用__slots__取代实例的__dict__
当你定义__slots__后,__slots__就会为实例使用一种更加紧凑的内部表示。实例通过一个很小的固定大小的数组来构建,而不是为每个实例定义一个
字典,这跟元组或列表很类似。在__slots__中列出的属性名在内部被映射到这个数组的指定小标上。使用__slots__一个不好的地方就是我们不能再给
实例添加新的属性了,只能使用在__slots__中定义的那些属性名。
4.注意事项:__slots__的很多特性都依赖于普通的基于字典的实现。另外,定义了__slots__后的类不再 支持一些普通类特性了,比如多继承。大多数情况下,你应该
只在那些经常被使用到 的用作数据结构的类上定义__slots__比如在程序中需要创建某个类的几百万个实例对象 。
关于__slots__的一个常见误区是它可以作为一个封装工具来防止用户给实例增加新的属性。尽管使用__slots__可以达到这样的目的,但是这个并不是它的初衷。           更多的是用来作为一个内存优化工具。

'''
class Foo:
    __slots__='x'


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

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

f1=Foo()
f1.name='alex'
f1.age=18
print(f1.__slots__)

f2=Foo()
f2.name='egon'
f2.age=19
print(f2.__slots__)

print(Foo.__dict__)
#f1与f2都没有属性字典__dict__了,统一归__slots__管,节省内存

__doc__

可以查看一个类的文档注释,无法被继承

__module__和__class__

__module__ 表示当前操作的对象在那个模块

__class__     表示当前操作的对象的类是什么

 __del__

析构方法,当对象在内存中被释放时,自动触发执行。

当程序结束时,python只会回收自己的内存空间,即用户态内存,而操作系统的资源则没有被回收,这就需要我们定制__del__,在对象被删除前向操作系统发起关闭数据库链接的系统调用,回收资源

class Foo:

    def __del__(self):
        print('执行我啦')

f1=Foo()
del f1
print('------->')

#输出结果
执行我啦
------->

 __call__

对象后面加括号,触发执行。

注:构造方法的执行是由创建对象触发的,即:对象 = 类名() ;而对于 __call__ 方法的执行是由对象后加括号触发的,即:对象() 或者 类()()

class Foo:

    def __init__(self):
        pass
    
    def __call__(self, *args, **kwargs):

        print('__call__')


obj = Foo() # 执行 __init__
obj()       # 执行 __call__

__next__和__iter__实现迭代器协议

实现斐波那契数列

class Fb():
    def __init__(self):
        self.x = 0
        self.y =1
    def __iter__(self):
        return self
    def __next__(self):
        if self.y > 100:
            raise StopIteration("大于100,停止")
        self.x, self.y = self.y, self.x+self.y
        return self.x

f1 = Fb()
print(next(f1))
print(next(f1))
print('===>')
for i in f1: # iter(f1) -->  __iter__()  __next__()
    print(i)

描述符(__get__,__set__,__delete__)

参考

https://www.cnblogs.com/linhaifeng/articles/6204014.html

猜你喜欢

转载自www.cnblogs.com/dreamer-lin/p/11624512.html