• 面向对象进阶


    1.反射

    2.双下方法

    反射


    1 什么是反射

    反射的概念是由Smith在1982年首次提出的,主要是指程序可以访问、检测和修改它本身状态或行为的一种能力(自省)。这一概念的提出很快引发了计算机科学领域关于应用反射性的研究。它首先被程序语言的设计领域所采用,并在Lisp和面向对象方面取得了成绩。

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

    四个可以实现自省的函数

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

    class Teacher:
        dic = {'查看老师信息':'show_teacher','查看学生信息':'show_studend'}
    
        def show_teacher(self):
            print('show teacher')
    
        def show_studend(self):
            print('show_studend')
    
    alex = Teacher()
    
    for k in Teacher.dic:
        print(k)
    key = input('you want: ')
    if hasattr(alex,Teacher.dic[key]):
        ret = getattr(alex,Teacher.dic[key])   #获取到内存地址
    ret()
    #alex.show_studend()
    反射初识
    #最重要的两个  hasattr  getattr
    # class B:pass
    # class A(B):pass
    # a = A()
    # print(isinstance(a,A))
    # print(isinstance('name',str))
    # print(issubclass(A,B))
    #
    # getattr()
    
    class A:
        price = 20
        def fuc(self):
            print('in func')
        @classmethod
        def func2(cls):
            print('method of class')
    
    a = A()
    a.name = 'gkx'
    # 变量名 = input('>>> ')
    # print(getattr(a,变量名))
    #print(a.__dict__[变量名])  #以下用 dict投机倒把而已
    #A.__dict__[变量名](a)
    
    # print(getattr(A,'price'))
    # if hasattr(A,'func2'):
    #     getattr(A,'func2')()
    
    
    #只要是  xx.yy 带点的,都可以反射
    
    
    year = 2018
    def wahaha():
        print('wahahahaha')
    import sys
    print(__name__)  #当__name__ 在本py运行的时候等于__main__ ,当被导入使用的时候,__name__等于 py文件名
    print(sys.modules)
    print(sys.modules[__name__])  #获取本py文件的模块地址,然后就可以反射了,
    getattr(sys.modules[__name__],'wahaha')()   #要用__name__   此处不能用'__main__'这样这段代码就写死了,其他地方如果导入这个py,用不了这段代码
    
    #只要是  xx.yy 带点的,都可以反射
    #可以反射模块
    import time
    
    # inp = input('>>> ') #输入time
    # print(getattr(time,inp)())
    # inp2 = input('>>> ') #输入localtime
    # ret = getattr(time,inp2)()
    # print(ret)
    # inp3 = input('>>> ') #输入asctime
    # print(getattr(time,inp3)(ret))
    
    #模块中的类也可以被我们拿到
    import for_import
    print(getattr(for_import,'C').__dict__)
    c = getattr(for_import,'C')()
    c.name = 'gkx'
    print(c.__dict__)
    
    
    
    #不重要的 setattr 和 delattr
    # setattr  设置,修改属性
    class A:pass
    a = A()
    setattr(A,'name','alex')
    setattr(a,'name','gkx')
    print(A.name)
    print(a.name)  #对象里有name,我就先找 name,不找类属性
    
    #delattr
    delattr(a,'name')  #对象里的name被删除了,就去找类里的,输出为 alex
    print(a.name)
    getattr,hasattr,setattr,delattr

    双下方法


    先了解这种编程思维,以后有机会可以来细究,双下方法用得好,有时候可以大大简化代码,及调高编程效率

    __str__  __repr__  __del__  __call__

    class B:pass
    class A(B):
        def __init__(self,name):
            self.name = name
    
        # def __str__(self):  #这个方法一定要return一个字符串
        #     return "A 里面的 双下str"
    
        def __repr__(self): #这个方法一定要return一个字符串
            return str(self.__dict__)
    a = A('gkx')
    print(a)  #没当你打印对象的时候,就是调用这个对象的双下str方法(若无,就找父类object) a.__str__
    print(str(a)) #此时调用的是 类A 父类 object里面的 __str__,它返回的是一个内存地址。
    #如何证明呢?
    #此时如果 类A里有个双下方法__str__,它就会优先调用 类A里的
    print('%s----%s'%('A',a))  # %s  print(a)  str(a)  都是在调用 a.__str__
    #注意,此处针对的都是 class A。
    
    print(str(a))    #调用的都是 双下repr,如果类中没有repr,就找父类要,父类没有继续找祖父类 object要
    print('%r'%a)
    
    #!!!!!!特大八卦
    # repr是str的备胎,当类A未定义双下str,但是类A中定义了 双下repr,此时调用str(a)就会调用类A中的双下 repr
    #但是反过来,如果类A中只有str,没有repr,则repr还是只会去找父类要双下repr
    #也就是说,str会去找类中的repr,repr只能自己找object
    #没str会找repr,没repr,只能回找boject
    
    #老师总结
    # print(obj)/'%s'%obj/str(obj)的时候,实际上是内部调用了obj.__str__方法,如果str方法有,那么他返回的必定是一个字符串
    # 如果没有__str__方法,会先找本类中的__repr__方法,再没有再找父类中的__str__。
    # repr(),只会找__repr__,如果没有找父类的
    
    #并不是所有内置方法都在object中,比如:
    #__len__就没有
    class Classes:
        def __init__(self,name):
            self.name = name
            self.student = []
        def __len__(self):   #当去掉这个方法,下面再调用 len 会报错!
            return len(self.student)
    python_s9  = Classes('py_s9')
    python_s9.student.append('gkx')
    print(len(python_s9))
    
    
    #__del__
    # class A:
    #     def __del__(self):   # 析构函数: 在删除一个对象之前进行一些收尾工作
    #         self.f.close()
    # a = A()
    # a.f = open()   # 打开文件 第一 在操作系统中打开了一个文件 拿到了文件操作符存在了内存中
    # del a          # a.f 拿到了文件操作符消失在了内存中
    # del a   # del 既执行了这个方法,又删除了变量
    # 引用计数
    
    
    # __call__
    class A:
        def __init__(self,name):
            self.name = name
        def __call__(self):
            '''
            打印这个对象中的所有属性
            :return:
            '''
            for k in self.__dict__:
                print(k,self.__dict__[k])
    a = A('alex')()   #一个对象加上括号,就是调用了 双下call方法,如果类中没双下call,就会报错
    双下方法-str,repr,del,call

     python2中是调用 __unicode__

    __hash__  __eq__

    # __eq__方法
    class A:
        def __init__(self,name):
            self.name = name
    
        def __eq__(self, other):
            if self.__dict__ == other.__dict__:
                return True
            else:
                return False
    a = A('gkx')
    a2 = A('gkx')
    print(a == a2) ##如果没有eq方法,是比较内存地址,会返回False
    
    
    #  __hash__
    class B:
        def __init__(self,name,age):
            self.name = name
            self.age = age
    
        def __hash__(self):
            return hash(self.name+self.age) #通过双下哈希,控制当属性一样哈希值是一样的
    b = B('gkx','11')
    b1 = B('gkx','11')
    print(hash(b)) #在一个程序执行过程中,某个变量的哈希值一直不会改变
    print(hash(b))
    print(hash(b1))
    双下方法hash,eq

    __new__

    # __new__ 构造方法,创建一个对象。在实例化的时候,就调用了object里的__new__创建了self
    # __init__ 初始化方法
    
    class A:
        def __init__(self):
            self.x = 1
            print('in the __init__ ')
    
        def __new__(cls, *args, **kwargs):
            print('in the __new__ ')
            return object.__new__(A,*args, **kwargs)
    
    a = A()   #先打印 in the new    再打印 in the init
    双下方法-new-面试考题
    #23中设计模式
    #单例模式
    # 一个类 始终 只有 一个 实例
    # 当你第一次实例化这个类的时候 就创建一个实例化的对象
    # 当你之后再来实例化的时候 就用之前创建的对象
    
    class B:
        __isinstance = False
        def __init__(self,name,age):
            self.name = name
            self.age = age
    
        def __new__(cls, *args, **kwargs):
            if cls.__isinstance:
                return cls.__isinstance
            cls.__isinstance = object.__new__(cls)
            return cls.__isinstance
    aa = B('gkx',11)
    aa.cloth = '1111111'
    bb = B('ww',22)
    print(aa,bb)  #内存空间是一模一样的
    print(aa.name)
    print(bb.name)  #所以name的值根据后面一个实例化而得
    print(bb.cloth)
    单例模式

    __getitem__  __setitem__  __delitem__

    class Foo:
        def __init__(self,name,age,sex):
            self.name = name
            self.age = age
            self.sex = sex
    
        def __getitem__(self, item):
            if hasattr(self,item):   #判断self,这个对象里是否有 item这个属性
                print(self.__dict__[item])
        def __setitem__(self, key, value):#类似字典的新增
            self.__dict__[key] = value
            #print(key,value)
    
        def __delitem__(self, key):
            del self.__dict__[key]
    
    f = Foo('gkx',12,'male')
    f['name']   #对于 双下getitem,当 obj[item]这种格式的时候,中括号里的值,自动传入方法内的参数 item,语法就是这么规定的
    f['hobby'] = 'ww'  #类似字典的新增,语法规定,hobby传给key ,ww传给value
    print(f.__dict__)
    # del f.hobby    #原生删除方法
    del f['hobby']
    print(f.__dict__)
    双下方法-item

    我们在使用内置函数的时候,很多时候都是调用内置双下方法

    内置函数  内置模块  内置数据类型 其实和双下方法有着千丝万缕的关系:但是只能在以后学习工作中慢慢积累,

    我们来看看下面这两个练习:

    练习一:

    在这个纸牌游戏中,利用双下方法,就定义了纸牌游戏,不用自己写方法

    同时了解到使用 random模块的时候,choice需要用到__len__获取长度,  shuffle需要用到 __setitem__ 乱序的时候需要获取到 index

    import json
    from collections import namedtuple
    
    Card = namedtuple('card',['rank','suit'])
    ranks = [str(n) for n in range(2,11)] + list('JQKA')
    suits = ['红心','黑桃','梅花','方块']
    class Franchdeck:
        def __init__(self):
            self._card = [Card(rank,suit) for rank in ranks        # for suit in suits:
                                          for suit in suits]            #for rank in ranks:  #在推导式中写后面的放前面
    
        def __getitem__(self, item):      # item就是你在类外想调用的index,在字典中就是key
            return self._card[item]
    
        def __len__(self):                #在导入random模块的时候,使用choice和shuffle需要用到双下len
            return len(self._card)
    
        def __setitem__(self, key, value):  #乱序的时候需要setitem,是因为乱序需要用到索引,把索引打乱,但是对应的值不能错
            self._card[key] = value
    
        def __str__(self):
            return json.dumps(self._card,ensure_ascii=False) # ensure_ascii=False 取消中文转义
            #return str(self._card) # 这样直接打印 print(deck)就不是内存地址了  str(deck)  %s
    deck = Franchdeck()
    print(deck[0])
    import random
    print(random.choice(deck))
    
    random.shuffle(deck)
    print(deck[:5])
    print(deck)  #可以获取整个列表的值,上面的切片就是从这里切的
    
    
    #以下没用,自己瞎写的
    # c1 = Card(2,'红心')
    # print(c1)
    # print(c1.rank)
    # print(getattr(c1,'rank'))
    # print(c1[1])
    #
    # lst = []
    # dic1 = {}
    # ranks = [str(n) for n in range(2,11)] + list('JQKA')
    # for suit in ['红心','黑桃','方块','梅花']:
    #     for rank in ranks:
    #         lst.append(Card(rank,suit))
    # print(lst)
    # print(dic1)
    纸牌游戏

    练习二:

    set集合去重,依赖__eq__ 以及 __hash__

    class A:
        def __init__(self,name,age,sex):
            self.name = name
            self.age = age
            self.sex = sex
    
        def __eq__(self, other):
            if self.name == other.name and self.sex == other.sex:
                return True
            return False
    
        def __hash__(self):
            return hash(self.name+self.sex)
    
        # def __str__(self):
        #     return str(self.__dict__)
    
    a = A('egg',11,'')
    a2 = A('egg',12,'')
    print(set([a,a2]))
    p_lst = []
    for i in range(84):
        p_lst.append(A('egg',i,''))
    print(p_lst)
    print(set(p_lst))
    # for i in p_lst:   #关于读取内存地址内容,如果是函数就直接加括号执行,把内存地址等价于变量名即可
    #     print(i.name)
    去重-set依赖eq和hash
  • 相关阅读:
    ZJOI2017 Day3 滚粗记
    ZJOI2017 Day2
    bzoj4245 [ONTAK2015]OR-XOR (贪心)
    bzoj4631 踩气球 (树状数组+线段树)
    bzoj5219 [Lydsy2017省队十连测]最长路径 (DP)
    bzoj5216 [Lydsy2017省队十连测]公路建设 (线段树)
    bzoj2754 [SCOI2012]喵星球上的点名 (后缀数组+树状数组)
    bzoj2342 [Shoi2011]双倍回文 (manacher)
    bzoj4657 tower (最小割)
    bzoj2064 分裂 (状压dp)
  • 原文地址:https://www.cnblogs.com/gkx0731/p/9650097.html
Copyright © 2020-2023  润新知