• python ——面向对象进阶(反射,双下线方法,静态方法,类方法)


    属性

    如果你已经了解Python类中的方法,那么属性就非常简单了,因为Python中的属性其实是普通方法的变种。

      

      哎,其实就是这样,我们看一下当我们想查看税后工资的时候,这其实是一个人的属性,但是它却要经过一些特殊的处理或者计算,让我们必须写在一个方法里,这个时候我们使用@property这个修饰符,就可以像使用字段一样调用这个方法了。对,属性是处女座就是这么矫情!!!当然如果你和我一样是神经大条的射手座,完全不用care这个属性什么鬼的。

      尽管如此,在这里还是要说一说要注意一下几点:

        1.定义时,在普通方法的基础上添加 @property 装饰器;

        2.定义时,属性仅有一个self参数

        3.调用时,无需括号

    1.staticmethod和classmethod

    staticmethod  静态方法: 让类里的方法,直接被类调用,就像正常的函数一样

    宝宝,男
    博博,女
    海娇,男
    海燕,女
    海东,男
    海峰,男
    student.txt
    class Student:
        # f = open('student', encoding='utf-8')
        def __init__(self):
            pass
    
        @staticmethod  
        def show_student_info():
            f = open('student', encoding='utf-8')  #静态方法不能和类属性直接交互,如果f放在第2行,f将作为类属性,那么下面的f将不能被调用
            for line in f:                         
                name, sex = line.strip().split(',')
                print(name, sex)
    Student.show_student_info()    

    classmethod  类方法: 默认参数cls,可以直接用类名调用,与类属性交互

    class Student:
        f = open('student', encoding='utf-8')
        def __init__(self):
            pass
        @classmethod #类方法: 默认参数cls,可以直接用类名调用,与类属性交互
        def show_student_info(cls):
            for line in cls.f:
                name, sex = line.strip().split(',')
                print(name, sex)
    
    Student.show_student_info()

    classmethod和staticmethod的异同:

    相同点:都可以直接被类调用,不需要实例化
    不同点:
        类方法(classmethod)必须有一个参数cls表示这个类,可以使用类属性
        静态方法(staticmethod)不需要参数,但是静态方法不能直接使用类属性

    方法包括:普通方法、静态方法和类方法,三种方法在内存中都归属于类,区别在于定义和调用的方式不同。

    • 普通方法:由对象调用;至少一个self参数;执行普通方法时,自动将调用该方法的对象赋值给self
    • 类方法:由调用; 至少一个cls参数;执行类方法时,自动将调用该方法的复制给cls
    • 静态方法:由调用;无默认参数;

    2.绑定方法和非绑定方法

    绑定方法:普通方法、类方法

      普通方法:默认有一个self对象传进来,并且只能被对象调用————绑定到对象
      类方法:默认有一个cls传进来表示本类,并且可以被类和对象(不推荐)调用————绑定到类

    非绑定方法:  

      静态方法:没有默认参数,并且可以被类和对象(不推荐)调用————非绑定

    3.isinstance 和 issubclass

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

    issubclass(sub, super)检查sub类是否是 super 类的派生类 

    class Foo:
        pass
    class Son(Foo):
        pass
    s=Son()
    print(isinstance(s,Son)) #True
    print(isinstance(s,Foo)) #True
    print(type(s) is Son) #False
    print(type(s) is Foo)  #False
    #isinstance和type的区别在于:当存在继承时,isinstance在判断时比较模糊,type则不会存在这个问题
    print(issubclass(Son,Foo)) #True
    print(issubclass(Son,object)) #True
    print(issubclass(Foo,object)) #True
    print(issubclass(int,object)) #True

    4.反射

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

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

    四个可以实现自省的函数

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

    常用:hasattr、getattr

    不常用:setattr、delattr

    class Foo:
        f = '类的静态变量'
        def __init__(self,name,age):
            self.name=name
            self.age=age
    
        def say_hi(self):
            print('hi,%s'%self.name)
    
    obj=Foo('egon',73)
    
    #检测是否含有某属性
    print(hasattr(obj,'name'))   #True
    print(hasattr(obj,'say_hi'))  #True
    
    #获取属性
    n=getattr(obj,'name')
    print(n)    #egon
    func=getattr(obj,'say_hi')
    func()     # hi,egon
    
    # print(getattr(obj,'aaaaaaaa','不存在啊')) #报错
    
    #设置属性
    setattr(obj,'sb',True)
    setattr(obj,'show_name',lambda self:self.name+'sb')
    print(obj.__dict__)   #{'name': 'egon', 'age': 73, 'sb': True, 'show_name': <function <lambda> at 0x0097C5D0>}
    print(obj.show_name(obj))   #egonsb    
    #用setattr为对象设置方法时:需要在()内手动传入绑定的对象,不能直接obj.show_name(),会报错
    
    #删除属性
    delattr(obj,'age')
    delattr(obj,'show_name')
    # delattr(obj,'show_name111')#不存在,则报错
    
    print(obj.__dict__)   #{'name': 'egon', 'sb': True}
    四中方法的使用演示

    在对象中应用反射

    class Foo:
        def __init__(self,name,age):
            self.name=name
            self.age=age
        def func(self):
            print(123)
    
    egg=Foo('hc',73)
    print(egg.name)   #hc
    print(egg.__dict__) #可以查看类的属性,不能查看方法   #{'name': 'hc', 'age': 73}
    print(egg.__dict__['name'])  #hc
    
    
    # 常用
    # hasattr  判断对象是否存在某属性或方法
    # getattr  如果存在这个方法或者属性,就返回属性值或者方法的内存地址;如果不存在,报错
    
    print(hasattr(egg,'name'))  #True
    print(getattr(egg,'name'))  #hc
    print(hasattr(egg,'func'))  #True
    print(getattr(egg,'func'))  #<bound method Foo.func of <__main__.Foo object at 0x00E2FBF0>>
    
    
    if hasattr(egg,'func'):   #返回bool
        Foo_func=getattr(egg,'func')  #如果存在这个方法或者属性,就返回属性值或者方法的内存地址
                                    #如果不存在,报错,因此要配合hasattr使用
        Foo_func()     #123

    在类中应用反射

    class Foo:
        f=123  #类变量
    
        @classmethod
        def class_method_demo(cls):
            print('class_method_demo')
    
        def class_method_demo1(self):
            print('class_method_demo1')
    
    if hasattr(Foo,'f'):
        print(getattr(Foo,'f'))
    
    print(hasattr(Foo,'class_method_demo'))  #True
    print(hasattr(Foo,'class_method_demo1'))  #True
    method=getattr(Foo,'class_method_demo')
    method1=getattr(Foo,'class_method_demo1')
    method()  #class_method_demo
    method1() #TypeError: class_method_demo1() missing 1 required positional argument: 'self'
    
    #类也是对象
    类也是对象

    在模块中应用反射 : 本模块和导入的模块

    def test():
        print('test')
    my_module.py
    import  my_module
    print(hasattr(my_module,'test'))  #True
    func_test=getattr(my_module,'test')
    func_test() #test
    getattr(my_module,'test')() #test   #和上面注释掉的2行等价
    import 其他模块应用反射
    def demo1():
        print('demo1')
    import sys
    # print(sys.modules) # 一个模块字典,
    # print(__name__)  #__main__
    # print(sys.modules[__name__])  #返回本py文件里的模块  <module '__main__' from 'E:/PycharmProjects/untitled/8.16/6反射3.py'>
    module_obj=sys.modules[__name__] #sys.modules[__main__]
    print(hasattr(module_obj,'demo1'))  #True    #在本模块中找函数demo1
    getattr(module_obj,'demo1')()      #demo1
    在本模块中应用反射

    5  __str__,__repr__

    先来一段代码,再来引入我们的正题:

    lst = list([1,2,3,4])
    class Foo:
        def __init__(self,name):
            self.name = name
    
    f = Foo('xiaohua')
    
    print(lst)
    print(f)
    
    ######输出结果#########
    [1, 2, 3, 4]
    <__main__.Foo object at 0x0000005FE8BC9208>

    我们都知道在python里面,list是一种数据类型,而且也是一个类。当我们以list()来创建一个序列时,我们也就创建了一个List对象。如上面的lst,知道了lst是List类型的对象后,我们print(lst)时,为什么没有像f一样,打印出对象的内存地址,而是直接打印出给对象赋的值呢?其实,这一切是因为在list类的内部,实现了__str__功能所导致的。下面,我们通过一些实例,来理解__str__的用法。

    class Foo:
        def __init__(self,name):
            self.name = name
        def __str__(self):
            return '我执行了'
    
    f = Foo('xiaohua')
    print(f)
    
    #######输出结果############
    我执行了

    看,当我再次打印对象的时候,没有打印出对象的内存地址,而是打印出了我们设定的值。__str__就是帮我们实现这种功能的!我们可以定制自己__str__,让他返回一些有意义的信息。

    def __str__(self):
            return 'name:%s' % self.name
    
      print(f)
    
      ####输出结果#####
      name:xiaohua
    class Foo:
        def __init__(self,name):
            self.name = name
        def __str__(self):
            return '%s obj info in str'%self.name
        def __repr__(self):
            return 'obj info in repr'
    
    f = Foo('hc')
    print(f)    # hc obj info in str
    print('%s'%f)   # hc obj info in str
    print('%r'%f)    #obj info in repr
    print(repr(f))  #repr(f)等价于f.__repr__()   # obj info in repr
    print(str(f))   #str(f)等价于f.__str__()    # hc obj info in str

    __str__和__repr__必须return 字符串
    当打印一个对象的时候,如果实现了str,打印__str__中的返回值,当__str__没有被实现的时候,就会调用__repr__方法(即__str__和__repr__同时存在时,会执行str,不会执行repr)
    但是当你用字符串格式化的时候 %s和%r会分别去调用__str__和__repr__
    不管是在字符串格式化的时候还是在打印对象的时候,repr方法都可以作为str方法的替补
    但反之不行
    用于友好的表示对象。如果str和repr方法你只能实现一个:先实现repr

    6 __new__,__del__

    __new__:创建对象时被调用,比__init__先执行。

    class Foo(object):
        def __init__(self):
            print('init')
    
        def __new__(cls, *args, **kwargs):
            print('new %s' %cls)
            return object.__new__(cls, *args, **kwargs)
    Foo()
    
    ###输出结果#######
    new <class '__main__.Foo'>  # 比init先打印
    init

     从上面,我们可以总结出如下几点:   

        继承自object的新式类才有__new__

        __new__至少要有一个参数cls,代表要实例化的类,此参数在实例化时由Python解释器自动提供

        __new__必须要有返回值,返回实例化出来的实例,这点在自己实现__new__时要特别注意,可以return父类__new__出来的实例,或者直接是object的__new__出来的实例

        __init__有一个参数self,就是这个__new__返回的实例,__init__在__new__的基础上可以完成一些其它初始化的动作,__init__不需要返回值

      若__new__没有正确返回当前类cls的实例,那__init__是不会被调用的,即使是父类的实例也不行

    class A(object):
        pass
    
    class B(A):
        def __init__(self):
            print('init')
    
        def __new__(cls, *args, **kwargs):
            print('new %s' % cls)
            return object.__new__(A, *args, **kwargs)
    b = B()
    print(type(b))
    
    输出结果:
    new <class '__main__.B'>
    <class '__main__.A'>
    class A:
        def __init__(self):  #有一个方法在帮你创造self
            print('in init function')
            self.x = 1
    
        def __new__(cls, *args, **kwargs):
            print('in new function')
            return object.__new__(cls, *args, **kwargs)
    a = A()
    b = A()
    c = A()
    print(a,b,c)
    
    ################单例模式###############
    class Singleton:
        def __new__(cls, *args, **kw):
            if not hasattr(cls, '_instance'):
                cls._instance = object.__new__(cls, *args, **kw)
            return cls._instance
    
    one = Singleton()
    two = Singleton()
    three = Singleton()
    print(one,two,three)
    
    one.name = 'alex'
    print(two.name)
    
    ######结果如下########
    in new function
    in init function
    in new function
    in init function
    in new function
    in init function
    <__main__.A object at 0x0297F6F0> <__main__.A object at 0x0297FB50> <__main__.A object at 0x0297FC10>
    <__main__.Singleton object at 0x0297FC70> <__main__.Singleton object at 0x0297FC70> <__main__.Singleton object at 0x0297FC70>
    alex

    __del__:在对象被删除或者程序执行完毕后,被调用

    # 以下代码执行完毕后,自动执行del
    class B:
        def __del__(self):
            print('我执行了')
    b = B()
    
    ######输出结果#########
    我执行了
    #显式调用del来删除对象时,触发__del__执行
    import time
    class B:
        def __del__(self):
            print('我执行了')
    b = B()
    del b # 触发__del__执行
    time.sleep(5)
    print('主程序结果')
    
    ###输出结果####
    我执行了
    #等待5秒
    主程序结果

    7.item系列

    __getitem__\__setitem__\__delitem__

    我们在列表中学过这种取元素的方式。比如说lst = [1,2,3,4],取第一个元素 lst[0],又或者在字典中dd ={'name':'xiaohua'} 取元素dd['name']。实际上,这种取元素的方式,与__getitem__,__setitem__,__delitem__三个函数有关。

      下面,我们一一来看看上述三个函数的用法:

      __getitem__:当我们想要按照 obj[attr]方式调用对象的属性时,触发这个函数的执行

    class Foo:
        def __init__(self,name):
            self.name = name
        def __getitem__(self, item):
            print('我执行了')
    
    f = Foo('alex')
    print(f.__dict__) # 对象的名称空间只有一个属性
    f['name']  # 触发getitem执行
    
    #####输出结果########
    {'name': 'alex'}
    我执行了

    从上面结果,可以看出,当我试图以f['name']方式调用属性的时候,就会触发__getitem__执行。一般将__getitem__设置为如下:

    class Foo:
        def __init__(self,name):
            self.name = name
        def __getitem__(self, item):
                print(self.__dict__[item])
    
    f = Foo('alex')
    print(f.__dict__)
    f['name']
    ########输出结果#######
    {'name': 'alex'}
    alex

    __setitem__:当我以f['name'] = 'xiaohua' 方式修改对象属性值的时候,会触发该函数的执行。

    class Foo:
        def __init__(self,name):
            self.name = name
    
        def __setitem__(self, key, value):
            print('我执行了')
    
    f = Foo('alex')
    print(f.__dict__)
    f['x'] = 3  # 触发setitem执行
    
    #########输出结果#########
    我执行了

    一般将setitem设置为如下,当然你也可以按照自己的方式进行设置。

    class Foo:
        def __init__(self,name):
            self.name = name
    
        def __setitem__(self, key, value):
             self.__dict__[key] = value
    
    f = Foo('alex')
    print(f.__dict__)
    f['x'] = 3
    print(f.__dict__)
    
    ###输出结果#######
    {'name': 'alex'}
    {'name': 'alex', 'x': 3}

    __delitem__:当以del f['name'] 方式删除对象的属性值时,会触发這个函数的执行

    class Foo:
        def __init__(self,name):
            self.name = name
    
        def __delitem__(self, key):
            print('我执行了')
    
    
    f = Foo('alex')
    print(f.__dict__)
    del f['name']  # 触发delitem执行
    
    ####输出结果#####
    {'name': 'alex'}
    我执行了

    一般设置为如下方式:

    class Foo:
        def __init__(self,name):
            self.name = name
    
        def __delitem__(self, key):
            del f.__dict__[key]
    
    
    f = Foo('alex')
    print(f.__dict__)
    del f['name']
    print(f.__dict__)
    #########输出结果####

    {'name': 'alex'}
    {}

    8__call__

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

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

    class Foo:
    
        def __init__(self):
            pass
        
        def __call__(self, *args, **kwargs):
    
            print('__call__')
    
    
    obj = Foo() # 执行 __init__
    obj()       # 执行 __call__   Foo()()等价于obj()

    9 __len__

    class A:
        def __init__(self):
            self.a = 1
            self.b = 2
    
        def __len__(self):
            return len(self.__dict__)
    obj = A()
    print(len(obj))

    http://www.cnblogs.com/wupeiqi/p/4766801.html

  • 相关阅读:
    Java中关系操作符==的学习以及与equals的对比
    关于alibaba.fastjson的使用
    给有C或C++基础的Python入门 :Python Crash Course 1
    快速幂基本模板
    断言封装及应用(有难度)
    断言封装之key检查及kv实战示例
    正则取值及断言实战示例
    关联实现下-jsonpath取值(有难度!!耗时长)
    关联实现上-jsonpath取值
    requests顺序执行实现
  • 原文地址:https://www.cnblogs.com/huchong/p/7373952.html
Copyright © 2020-2023  润新知