• 面向对象知识点


     

    @(python)[笔记]

    目录

    一、isinstance()和issubclass()
        1. isinstance()
        2. issubclass()
    
    二、反射
        1. hasattr()
        2. getattr()
        3. setattr()
        4. delattr()
        5. 扩展用法
    
    三、__setattr__、__delattr__、__getattr__
    四、二次加工标准类型(包装)
        授权
    五、__next__和__iter__实现迭代器协议
    六、__doc__
    七、__module__和__class__
    八、__str__
    九、__del__析构方法
    十、__setitem__,__getitem__,__delitem__
    十一、__enter__和__exit__
    十二、__call__
    十三、元类metaclass
        先来看看exec
        未完

    一、isinstance()和issubclass()

    1. isinstance()

    语法:isinstance(obj,cls)
    功能:检查对象obj是否是类cls的实例

    class foo:
        pass
    obj = foo()
    print(isinstance(obj,foo))  #True

    2. issubclass()

    语法:issubclass(sub, super)
    功能:检查sub类是否是super类的派生类(子类)

    class foo:
        pass
    obj = foo()
    print(isinstance(obj,foo))  #True
    
    class bar(foo):
        pass
    print(issubclass(bar,foo))  #True

    二、反射

    反射主要是指程序可以访问、检测和修改它本身状态或行为的一种能力,也可以称为自省

    python面向对象中的反射是指通过字符串的形式操作对象相关的属性。python中的一切事物都是对象,都可以使用反射。

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

    下列方法适用于类和对象(一切皆对象,类本身也是一个对象)

    1. hasattr()

    语法:hasattr(object,"name")
    功能:判断object中有没有一个name字符串对应的方法或属性。
    用法:

    class foo:
        name = "egon"
        pass
    
    obj = foo()
    print(hasattr(obj,"name"))  #True
    print(hasattr(obj,"func"))  #True
    print(hasattr(foo,"name"))  #True
    print(hasattr(foo,"func"))  #True

    2. getattr()

    语法:getattr(object, "name"[, default=None])
    功能:从对象object获取一个name字符串对应的方法,如果没有name字符串对应的方法,并且没有设置default,则报错;如果没有name字符串对应的方法,设置了default,则将default的值返回。getattr(object,"name")等价于object.name

    class foo:
        name = "egon"
        def func(self):
            print("Hello world")
    
    obj = foo()
    print(getattr(obj,"func"))
        #<bound method foo.func of <__main__.foo object at 0x000000000112ACC0>>
        #可以看出,输出结果是一个绑定方法
    
    f1 = getattr(obj,"func")
    f2 = getattr(obj,"bar","不存在")
    f1()   #Hello world
    print(f2)  #不存在

    3. setattr()

    语法:setattr(x, y, v)
    功能:设置属性,setattr(x,'y',v)等价于“x.y = v
    用法:

    class foo:
        name = "egon"
        def func(self):
            print("Hello world")
    
    obj = foo()
    setattr(obj,"age",20)
    setattr(obj,"show_name",lambda self:self.name+"_NB")
    print(obj.__dict__)   #查看属性字典
    print(obj.show_name(obj))
    
    '''
    输出:
    {'age': 20, 'show_name': <function <lambda> at 0x000000000115E2F0>}
    egon_NB
    '''

    4. delattr()

    语法:delattr(x, y)
    功能:删除属性,delattr(x,'y')相当于``del x.y''
    用法:

    delattr(obj,"show_name")
    # delattr(obj,"sex")   #不存在,则报错
    print(obj.__dict__) #{'age': 20}

    5. 扩展用法

    反射当前模块

    import sys
    def s1():
        print("s1")
    
    def s2():
        print("s2")
    
    this_module = sys.modules[__name__]
    print(hasattr(this_module,"s1"))   #True
    print(getattr(this_module,"s2"))
        #<function s2 at 0x00000000006EE378>,加括号可执行

    导入其他模块,利用反射查找该模块是否存在某个方法
    程序目录:

    • module_test.py
    • current.py
    # module_test.py
    def test():
        print("from the module_test.test")
    #current.py
    
    import module_test as mt
    
    print(hasattr(mt,"test"))   #True
    f = getattr(mt,"test","不存在")
    f()   #from the module_test.test

    三、__setattr____delattr____getattr__

    __setattr__ :添加/修改属性会触发它的执行;
    __delattr__ :删除属性的时候会触发;
    __getattr__ :只有在使用点调用属性且属性不存在的时候才会触发。

    class Foo:
        x = 1
        def __init__(self,y):
            self.y = y
    
        def __getattr__(self, item):
            print("---> 你找的属性不存在")
    
        def __setattr__(self, key, value):
            print("---> from __setattr__")
            # self.key = value  #这样会陷入无限递归,只能通过__dict__字典进行赋值
            self.__dict__[key] = value   #这样才可以正确赋值
    
        def __delattr__(self, item):
            print("---> from __delattr__")
    
    f1 = Foo(10)  #相当于设值,触发__setattr__执行,---> from __setattr__
    print(f1.__dict__)
        #{},直接打印为空,是因为你自己重写了__setattr__方法,
        # 而你在__setattr__方法中没有真正赋值
    
    f1.z          #触发__getattr__执行,---> 你找的属性不存在
    
    del f1.x      #触发__delattr__执行,---> from __delattr__

    四、二次加工标准类型(包装)

    包装:python为用户提供了标准数据类型,以及丰富的内置方法,其在很多场景下,我们需要基于标准数据类型来定制我们自己的数据类型,新增 / 改写方法,这就用到继承和派生的知识,其他标准类型均可以通过下面的方式进行二次加工。

    示例1:对list进行二次加工,限制append只能增加int整型数据;并且增加mid方法,得到列表的中间值;其余方法都继承list的。

    class List(list):
        def append(self,p_object):
            #派生出自己的append方法,会覆盖父类list中的append方法
            if not isinstance(p_object,int):
                raise TypeError("%s must be int"%p_object)
            super(List, self).append(p_object)
    
        @property
            #中间值听起来更像一个属性,而非方法,所以使用property
        def mid(self):
            mid_num = len(self) // 2
            return self[mid_num]
    
    
    l = List([1,2,3,4])
    print(l)             #[1, 2, 3, 4]
    l.append(5)
    print(l)             #[1, 2, 3, 4, 5]
    print(l.mid)         #中间值3

    示例二:为listclear方法增加权限

    class List(list):
        def __init__(self,item,perm=False):
            super(List, self).__init__(item)
            self.perm = perm
                #先设定一个默认的权限
    
        def clear(self):
            if not self.perm:
                raise PermissionError("权限拒绝")
            super(List, self).clear()
    
    l = List([1,2,3])
    print(l)          #[1, 2, 3]
    # l.clear()       #抛出“权限拒绝”的异常
    l = List([1,2,3],True)  #给一个授权参数为True
    l.clear()
    print(l)          #[],可以正常清空列表

    授权

    授权是包装的一个特性,包装一个类型通常是对已存在的类型的一些定制,这种做法可以新建、修改或删除原有产品的功能,其它的则保持原样。授权的过程就是所有更新的功能都是由新类的某部分来处理,但已存在的功能就授权给对象的默认属性。

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

    示例三:利用open()函数重新定制一个文件处理器,增加写内容添加时间的功能;

    import time
    
    class FileHandler:
        def __init__(self,filename,mode='r',encoding="utf-8"):
            self.file = open(filename,mode,encoding=encoding)
            #self.file获取到一个文件句柄
    
        def write(self,line):
            t = time.strftime("%Y-%m-%d %X")
            self.file.write("%s %s"%(t,line))
    
        def __getattr__(self, item):
            return getattr(self.file,item)
            #当对象调用FileHandler类不存在的方法时,会返回open()函数的item字符串对应的方法;
            
    
    f1 = FileHandler("a.txt","r+")
    f1.write("你好吗
    ")
    f1.seek(0)
    print(f1.tell())   #0

    示例四:

    #我们来加上b模式支持
    import time
    class FileHandle:
        def __init__(self,filename,mode='r',encoding='utf-8'):
            if 'b' in mode:
                self.file=open(filename,mode)
            else:
                self.file=open(filename,mode,encoding=encoding)
            self.filename=filename
            self.mode=mode
            self.encoding=encoding
    
        def write(self,line):
            if 'b' in self.mode:
                if not isinstance(line,bytes):
                    raise TypeError('must be bytes')
            self.file.write(line)
    
        def __getattr__(self, item):
            return getattr(self.file,item)
    
        def __str__(self):
            if 'b' in self.mode:
                res="<_io.BufferedReader name='%s'>" %self.filename
            else:
                res="<_io.TextIOWrapper name='%s' mode='%s' encoding='%s'>" %(self.filename,self.mode,self.encoding)
            return res
    f1=FileHandle('b.txt','wb')
    # f1.write('你好啊啊啊啊啊') #自定制的write,不用在进行encode转成二进制去写了,简单,大气
    f1.write('你好啊'.encode('utf-8'))
    print(f1)
    f1.close()

    示例五:利用授权的方式重新定制listappend方法,只能往列表中添加int整型数据;与示例一作对比,看一下两者的区别。

    
    #授权,定制list
    
    class List:
        def __init__(self,seq):
            self.seq = list(seq)
    
        def append(self,p_object):
            if not isinstance(p_object,int):
                raise TypeError("'%s' must be int"%p_object)
            self.seq.append(p_object)
    
        def __getattr__(self, item):
            return getattr(self.seq,item)
    
        def __str__(self):
            return str(self.seq)
    
    l = List([1,2,3])
    l.append("4")   #TypeError: '4' must be int
    print(l)

    总结:授权这种方式用在定制源不是类的情况下。例如示例三中,定制open()函数

    五、__next____iter__实现迭代器协议

    迭代器必须要有__next____iter__方法;
    现在就来自己实现一个迭代器吧;
    简单实现:

    class Foo:
        def __init__(self,x):
            self.x=x
    
        def __iter__(self):
            return self
    
        def __next__(self):
            n=self.x
            self.x+=1
            return self.x
    
    f=Foo(3)
    for i in f:
        print(i)

    模拟实现range()函数:

    class foo:
        def __init__(self,start,stop=None,step=1):
            self.start = start
            self.stop = stop
            self.step = step
            if isinstance(self.start,str) 
                    or isinstance(self.stop,str) 
                    or isinstance(self.step,str):
                raise InterruptedError("Must be Numeric")
    
        def __next__(self):
            if self.stop:
                res = self.compute()
            else:
                self.stop = self.start
                # 如果只传了一个数字,则将其设为迭代停止数字
                self.start = 0
                # 如果只传了一个数字,则默认从0开始迭代
                res = self.compute()
            return res
    
        def __iter__(self):
            return self
            #迭代器执行__iter__方法,返回的是它本身
    
        def compute(self):
            if self.start >= self.stop:
                #判断是否超出迭代停止数字
                raise StopIteration
                # 这是超出迭代器范围后迭代器协议规定的抛出异常
            iter_val = self.start     #迭代后的值
            self.start += self.step
            return iter_val
    
    for i in foo('a',20,3.5):
        print(i)
    
    for i in foo(5,15,3):
        print(i)

    六、__doc__

    查看对象的描述信息

    def func():
        '''我是函数的描述信息'''
        pass
    print(func.__doc__)
    
    class Foo:
        '''我是类的描述信息'''
        pass
    
    class bar(Foo):
        pass
    
    obj = Foo()
    print(obj.__doc__)
    print(Foo.__doc__)
    print(bar.__doc__)   #该属性不会继承给子类
    
    '''
    输出:
    我是函数的描述信息
    我是类的描述信息
    我是类的描述信息
    None
    '''

    七、__module____class__

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

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

    以下两个文件在同一级目录下:

    #current.py
    
    class C:
        def __init__(self):
            self.name = "alex"
    #test.py
    
    import current
    
    def test():
        print("from the test.test")
    
    obj = current.C()
    print(obj.name)
    print(obj.__class__)  #输出是哪个类实例化得到的对象
    print(obj.__module__) #输出属性哪个模块
    '''
    输出:
    alex
    <class 'current.C'>
    current
    '''

    八、__str__

    l = list([1,2,3])
    print(l)   #打印的是[1, 2, 3]
    class mysql:
        def __init__(self,host,port):
            self.host = host
            self.port = port
    
    conn = mysql("127.0.0.1","3306")
    print(conn) #打印的是<__main__.mysql object at 0x0000000000701898>

    从以上两段代码可以看到,我们自己定义的类,生成的对象直接被打印时,打印的是对象的内存地址,而这并不是我们想要的,我们实际想要的也是像第一段代码那样返回一个有用的信息,这时就要用到__str__这个内置方法了,它定义在类的内部,只要类被实例化,就会自动触发__str__的执行,并返回一个值给实例化后的对象。如下示例:

    class mysql:
        def __init__(self,host,port):
            self.host = host
            self.port = port
    
        def __str__(self):
            return "Host:%s,Port:%s"%(self.host,self.port)
    
    conn = mysql("127.0.0.1","3306")
    #会将`__str__`方法的返回值赋值给对象conn
    print(conn) 
    
    '''
    输出:
    Host:127.0.0.1,Port:3306
    '''

    九、__del__析构方法

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

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

    简单示例:

    class Foo:
        def __del__(self):
            print("执行__del__")
    
    f1 = Foo()
    del f1   #释放f1,会触发__del__执行
    print("------->")
    
    '''
    输出:
    执行__del__
    ------->
    '''

    再看看下面这种情况:

    class Foo:
        def __del__(self):
            print("执行__del__")
    
    f1 = Foo()
    # del f1    #注释掉
    print("------->")
    
    '''
    输出:
    ------->
    执行__del__
    '''
    #可以看出__del__仍然会在程序执行完毕后,被触发执行

    十、__setitem__,__getitem__,__delitem__

    类中有这三个方法,那就意味着类中的属性可以像字典一样进行操作;

    class Foo:
        def __init__(self,name):
            self.name = name
    
        def __getitem__(self, item):
            print("from __getitem__")
    
        def __setitem__(self, key, value):
            print("from __setitem__")
            self.__dict__[key] = value  #加入此代码,才会真正设值
    
        def __delitem__(self, key):
            print("from __delitem__")
            self.__dict__.pop(key)
    
    f1 = Foo("egon")
    # f1.age = 18     #不会触发__setitem__的执行
    f1["age_2"] = 20  #会触发__setitem__的执行,并不会真正设值;
    print(f1.__dict__)
    del f1["age_2"]
    print(f1.__dict__)
    print(f1["name"])   #会触发__getitem__执行,如果__getitem__没有设定返回值,则会返回一个None
    
    '''
    输出结果:
    from __setitem__
    {'name': 'egon', 'age_2': 20}
    from __delitem__
    {'name': 'egon'}
    from __getitem__
    None
    '''
    #从结果可以看出,age_2 = 20并没有真正设值成功

    十一、__enter____exit__

    通过__enter____exit__这两个方法可以实现上下文件管理协议,即with语句。为了让一个对象兼容with语句,必须在这个对象的类中声明__enter____exit__方法。

    class Open:
        def __init__(self,name):
            self.name = name
    
        def __enter__(self):
            #出现with语句,对象的__enter__方法就会被触发,有返回值则赋值给as声明的变量
            print("from __enter__")
            return 3   #会返回给as语句后面的变量
    
        def __exit__(self, exc_type, exc_val, exc_tb):
            print("from __exit__, with代码块执行完毕时触发")
    
    with Open("egon") as f:
        print("from with 代码块")
        print("f:",f)
    
    '''
    输出:
    from __enter__
    from with 代码块
    f: 3
    from __exit__, with代码块执行完毕时触发
    '''

    __exit__(self, exc_type, exc_val, exc_tb)中的三个参数的含义:

    • exc_type 代表异常类型
    • exc_val 代表异常值
    • exc_tb 代表异常的追溯信息
    class Open:
        def __init__(self,name):
            self.name = name
    
        def __enter__(self):
            #出现with语句,对象的__enter__方法就会被触发,有返回值则赋值给as声明的变量
            # print("from __enter__")
            return self   #会返回给as语句后面的变量
    
        def __exit__(self, exc_type, exc_val, exc_tb):
            print("from __exit__, with代码块执行完毕时触发")
            print("exc_type:",exc_type)
            print("exc_val:",exc_val)
            print("exc_tb:",exc_tb)
            return True  #返回True,则表示不抛出异常,with代码之后的代码可正常执行,但with子代码raise后的代码不会执行。
    
    with Open("egon") as f:
        print("from with 代码块")
        raise AttributeError("属性错误")
        # print("=====> 在异常之后")    #不会执行
    print("=====> 在异常之后,with之外")   #__exit__返回True才会执行
    
    '''
    输出:
    from with 代码块
    from __exit__, with代码块执行完毕时触发
    exc_type: <class 'AttributeError'>
    exc_val: 属性错误
    exc_tb: <traceback object at 0x014B58A0>
    =====> 在异常之后
    '''

    示例:模拟open(),并实现上下文管理

    #模拟open()功能
    
    class Open:
        def __init__(self,filename,mode='r',encoding="utf-8"):
            self.filename = filename
            self.mode = mode
            self.encoding = encoding
    
        def __enter__(self):
            self.file = open(self.filename,self.mode,encoding =self.encoding)
            return self.file
    
        def __exit__(self, exc_type, exc_val, exc_tb):
            self.file.close()
            return True  #遇到异常不抛出,with之外的代码继续执行
    
    with Open("a.txt","w+") as f:
        f.write("11111
    ")
        f.write("22222
    ")
        f.seek(0)
        print(f.tell())
        f.abc    #抛出异常,交给__exit__处理

    十二、__call__

    对象后面加括号,就会触发__call__方法的执行;反之类中有了__call__方法,通过这个类生成的对象,才能加括号执行。

    class foo:
        def __init__(self):
            print("from __init__")
    
        def __call__(self, *args, **kwargs):
            print("from __call__")
    
    obj = foo()    #执行__init__
    obj()          #执行__call__
    
    '''
    输出:
    from __init__
    from __call__
    '''
  • 相关阅读:
    中级实训Android学习记录——百度地图API显示定位
    中级实训Android学习记录——Android Studio工程配置并进行地图显示
    中级实训Android学习记录——百度地图API获取AK
    unity 3d 八、粒子系统与流动效果
    中级实训Android学习记录——项目3制作
    中级实训Android学习记录——RecyclerView、Android存储
    中级实训Android学习记录——项目1、2重铸记录
    裴蜀定理
    二分模板
    最短路问题
  • 原文地址:https://www.cnblogs.com/chenghao1994/p/7221935.html
Copyright © 2020-2023  润新知