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
__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 - 面试考题
__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
来源: http://www.bubuko.com/infodetail-2768478.html