Python基础(十二)-面向对象进阶

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

isinstance(obj,cls)检查是否obj是否是类 cls 的对象

issubclass(sub, super)检查 sub类 是否是 super 类的派生类(子类)  不是直接继承,隔代继承也为True

结果为 True or False

2、自省/反射

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

Python中的反射:通过字符串的形式操作对象相关的属性。python中的一切事物都是对象(都可以使用反射)包括类、模块

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

hasattr(obj,name)
判断一个obj中有没有一个为name的方法或属性

getattr(obj,name)
获取对象obj里name属性或方法

setattr(obj,name,value)
对象obj里name属性或方法设置值为value

delattr(obj,name)
删除对象obj属性或方法name

name都是字符串形式
class Open:
    a=110
    def __init__(self,name):
        self.name=name

    def fuck(self):
        return 1


print(hasattr(Open,'fuck'))     #True
print(getattr(Open,'fuck'))     #<function Open.fuck at 0x0000000000BCCF28>
print(getattr(Open,'a',))         #110
print(setattr(Open,'a',120))   #None
print(Open.a)                         #120
print(delattr(Open,'a'))
print(getattr(Open,'a'))          #AttributeError: type object 'Open' has no attribute 'a'

事例

反射当前模块成员

#!/usr/bin/env python
# -*- coding:utf-8 -*-

import sys


def s1():
    print 's1'


def s2():
    print 's2'


this_module = sys.modules[__name__]

hasattr(this_module, 's1')
getattr(this_module, 's2')

反射当前模块成员

动态导入模块

m = __import__('m1.t')  只导入了m1,没有t 但执行了t的代码  m.name-->只调用m1的属性

 

import importlib

m = importlib.import_module('m1.t')  导入到t

 

 

 3、__str__

如果一个类中定义了__str__方法,那么在打印 对象 时,默认输出该方法的返回值。

class Foo:
    
    def __str__(self):
        return 'fuck you too'


obj = Foo()
print(obj)    # 输出为 ‘fuck you too’
__str__

4、__del__ 析构方法

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

注:此方法一般无须定义,因为Python是一门高级语言,程序员在使用时无需关心内存的分配和释放,因为此工作都是交给Python解释器来执行,所以,析构函数的调用是由解释器在进行垃圾回收时自动触发执行的。

class Foo:

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

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

#输出结果
执行我啦
------->
析构方法

5、__call__

对象执行时触发,obj() 或 class()()

class Foo():
    def __init__(self):
        print('-->__init__')
    def __call__(self, *args, **kwargs):
        print('-->__call__')

obj=Foo()
obj()
__call__事例

6、__getattr__ __setattr__ __delattr__

对象点属性 操作方式

__getattr__   obj.name 当obj没有name属性时才触发自定义__getattr__方法

class F1():
    def __init__(self,a):
        self.a=a

    def __getattr__(self, item):
        print('----> from F1.__getattr__')
        return self.__dict__[item]


    def __setattr__(self, key, value):     # 对象赋值操作都会自动执行此方法
        print('----> from F1.__setattr__')
        self.__dict__[key]=value   # 此时对象属性字典才有值  #直接对属性字典进行操作

    def __delattr__(self, item):
        print('----> from F1.__delattr__')
        self.__dict__.pop(item)     #直接对属性字典进行操作

f1=F1(10)  # 内部对f1对象a属性进行赋值 触发自定义__setattr__方法  #----> from F1.__setattr__
print(f1.__dict__)   #{'a': 10}
print(f1.a)   # f1有a属性没有触发自定义__getattr__  #10
print(f1.b)   # f1没有b属性 触发了自定义__getattr__方法  #----> from F1.__getattr__  None

f1.b=110   # ----> from F1.__setattr__
print(f1.__dict__)  # {'b': 110, 'a': 10}
 
del f1.b   # 删除对象属性触发自定义__delattr__属性 #----> from F1.__delattr__
print(f1.__dict__) #{'a': 10}
事例

7、__getattribute__

类比于__getattr__,我觉得更应该是补充, obj.name 当obj没有name属性时才触发自定义__getattr__方法

__getattribute__ 不管有没有该属性都会执行自定义__getattribute__方法

class Foo:
    def __init__(self,x):
        self.x=x

    def __getattribute__(self, item):
        print('不管是否存在,我都会执行')

f1=Foo(10)
f1.x
f1.xxxxxx


##执行结果
不管是否存在,我都会执行
不管是否存在,我都会执行
事例

那么当两者同时存在时,执行顺序呢?---> 只会执行__getattrbute__,除非__getattribute__在执行过程中抛出异常AttributeError,然后再执行__getattr__

class Foo:
    def __init__(self,x):
        self.x=x

    def __getattribute__(self, item):
        print('不管是否存在,__getattribute__都会执行')
        raise AttributeError('fuck')

    def __getattr__(self, item):
        print('执行__getattr__')

f1=Foo(10)
f1.x
f1.xxxxxx

# 执行结果
不管是否存在,__getattribute__都会执行
执行__getattr__
不管是否存在,__getattribute__都会执行
执行__getattr__
事例

 

8、__getitem__ __setattr__ __delitem__

用于索引操作,obj[item] ,以上分别表示获取、设置、删除数据

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

    def __getitem__(self, item):
        print('-->__getitem__<--',self.__dict__[item])

    def __setitem__(self, key, value):
        print('---> from __setitem__ <---')
        self.__dict__[key]=value

    def __delitem__(self, key):
        print('del obj[key] 时,我执行')
        self.__dict__.pop(key)

    def __delattr__(self, item):
        print('del obj.key 时,我执行')
        self.__dict__.pop(item)

f1=Foo('sb')    #执行__setattr__

f1['age']=18    # 执行 __setitem__
f1['age1']=19
print(f1.__dict__)

del f1.age1    #执行 __delattr__
del f1['age']   # 执行__delitem__

f1['name']='alex'
print(f1.__dict__)
事例

 9、__iter__和__next__实现迭代器协议

class Foo1:
    def __init__(self,x):
        self.x=x

    def __iter__(self):
        return self

    def __next__(self):
        self.x-=1
        if self.x>=0:
            return self.x
        else:
            raise StopIteration

f1=Foo1(10)
for i in f1:
    print(i)
简单事例1
class Foo2:
    def __init__(self,start,stop,step):
        self.num=start
        self.stop=stop
        self.step=step
    def __iter__(self):
        return self
    def __next__(self):
        if self.num == self.stop:
            raise StopIteration
        n=self.num
        self.num+=self.step
        return n

f=Foo2(1,8,1)

from collections import Iterable,Iterator
print(isinstance(f,Iterator))

for i in f:
    print(i)
带步长
class Fib:
    def __init__(self):
        self._a=0
        self._b=1

    def __iter__(self):
        return self

    def __next__(self):
        self._a,self._b=self._b,self._a + self._b
        return self._a

f1=Fib()

print(f1.__next__())
print(next(f1))
print(next(f1))

for i in f1:
    if i > 100:
        break
    print('%s ' %i,end='')
斐波那契数列

10、__enter__和__exit__实现with

上下文管理协议,即with语句,为了让一个对象兼容with语句,必须在这个对象的类中声明__enter__和__exit__方法

class Open:

    def __init__(self,name): # 第2步
        self.name=name
        print(self.name)

    def __enter__(self): # 第3步
        print('出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量')
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):  # 第5步
        print('with中代码块执行完毕时执行我啊')
        print(exc_type)
        print(exc_val)
        print(exc_tb)
        return True

with OPEN('a.txt') as f:  # 第1步
    print('=====>执行代码块') # 第4步

 

__exit__()中的三个参数分别代表异常类型,异常值和追溯信息,with语句中代码块出现异常,则with后的代码都无法执行

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

    def __enter__(self):
        print('出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量')

    def __exit__(self, exc_type, exc_val, exc_tb):
        print('with中代码块执行完毕时执行我啊')
        print(exc_type)         #<class 'AttributeError'>
        print(exc_val)           #***着火啦,救火啊***
        print(exc_tb)            #<traceback object at 0x00000000011E8F08>



with Open('a.txt') as f:
    print('=====>执行代码块')
    raise AttributeError('***着火啦,救火啊***')
print('0'*100) #------------------------------->不会执行
View Code

如果__exit__()返回值为True,那么异常会被清空,就好像啥都没发生一样,with后的语句正常执行

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

    def __enter__(self):
        print('出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量')

    def __exit__(self, exc_type, exc_val, exc_tb):
        print('with中代码块执行完毕时执行我啊')
        print(exc_type)
        print(exc_val)
        print(exc_tb)
        return True



with Open('a.txt') as f:
    print('=====>执行代码块')
    raise AttributeError('***着火啦,救火啊***')
print('0'*100) #------------------------------->会执行
View Code
class Open:
    def __init__(self,filepath,mode='r',encoding='utf-8'):
        self.filepath=filepath
        self.mode=mode
        self.encoding=encoding

    def __enter__(self):
        # print('enter')
        self.f=open(self.filepath,mode=self.mode,encoding=self.encoding)
        return self.f

    def __exit__(self, exc_type, exc_val, exc_tb):
        # print('exit')
        self.f.close()
        return True 
    def __getattr__(self, item):
        return getattr(self.f,item)

with Open('a.txt','w') as f:
    print(f)
    f.write('aaaaaa')
    f.wasdf #抛出异常,交给__exit__处理
模拟open()

用途或者说好处:

1.使用with语句的目的就是把代码块放入with中执行,with结束后,自动完成清理工作,无须手动干预

2.在需要管理一些资源比如文件,网络连接和锁的编程环境中,可以在__exit__中定制自动释放资源的机制,你无须再去关系这个问题,这将大有用处

11、__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__
# print(f1.__dict__)   #AttributeError: 'Foo' object has no attribute '__dict__'
print(Foo.__dict__)   #{'__slots__': 'x', 'x': <member 'x' of 'Foo' objects>, '__module__': '__main__', '__doc__': None}


class Bar:
    __slots__ = ['x', 'y']


n = Bar()
n.x, n.y = 1, 2
# n.z = 3  # 报错
View Code

12、描述符(__get__、__set__、__delete__)

 

13、__metaclass__

 

http://www.thinksaas.cn/topics/0/666/666248.html

posted @ 2017-01-04 11:42  chenzhuo  阅读(224)  评论(0编辑  收藏  举报