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

 

posted on 2018-09-15 10:09  阿橙  阅读(114)  评论(0编辑  收藏  举报