反射的进阶

一、类的描述符

1、property :把装饰的一个方法伪装成一个属性,对属性进行查、改、删

#装饰器返回年纪
import time
class Person:
    def __init__(self,name,birth):
        self.name = name
        self.birth = birth
    @property
    def age(self):  # 返回年纪
        struct_t = time.localtime()
        age = struct_t.tm_year - int(self.birth.split('-')[0])
        return age
alex = Person('alex','1965-5-12')
print(alex.age)  #54

#装饰器返回
class Goods:
    discount = 0.8
    def __init__(self, name, price):
        self.name = name
        self.__price = price
    @property
    def price(self):  #私有变量虽然不可以按原本的方式访问,但是可以使用property,改变
        p =  self.__price * self.discount
        return p

apple = Goods('苹果', 5)  # {'name': '苹果', '_Goods__price': 5} 返回私有变量,
banana = Goods('香蕉', 10)  #{'name': '香蕉', '_Goods__price': 10}
print(apple.__dict__)
print(banana.__dict__)
print(apple.name)  # 苹果
print(apple.price)  # 4.0
print(banana.price)  #8.0
Goods.discount = 1
print(apple.price) #5
print(banana.price) #10

#属性.setter修改属性
class Goods:
    discount = 0.8
    def __init__(self, name, price):
        self.name = name
        self.__price = price
    @property    # 只支持obj.price的方式查看这个结果,不支持修改,也不支持删除
    def price(self):
        p =  self.__price * self.discount
        return p
    @price.setter # 其实这里是类似于纵向装饰器,先设置,在进入上面的peoperty,最后返回属性
    def price(self,value):
        self.__price = value

apple = Goods('苹果', 5)
banana = Goods('香蕉', 10)
print(apple.price) # 4
apple.price = 8   # 对应的调用的是被setter装饰的price方法
print(apple.price)  # 6.4 对应调用的是被property装饰的price方法

class Goods:
    discount = 0.8
    def __init__(self, name, price):
        self.name = name
        self.__price = price

    @property    # 只支持obj.price的方式查看这个结果,不支持修改,也不支持删除
    def price(self):
        p =  self.__price * self.discount
        return p

    @price.setter # 设置属性
    def price(self,value):
        if type(value) is int or type(value) is float:
            self.__price = value

     @price.deleter  # 删除属性   会自动删除属性
     def price(self):
         del self.__price

# 想删除一个属性
apple = Goods('苹果', 5)
print(Goods.__dict__)
print(apple.__dict__) #{'name': '苹果', '_Goods__price': 5}
apple.price
apple.price = 9 #设置
print(apple.price) #7.2
# del apple.price  #执行后 就没有price属性了

class Teacher:
    __num = 0
    def __init__(self,name,pw):
        self.name = name
        self.pw = pw
        self.__num += 1
    # def read_num():  # 报错,这里显示找不到属性
    #     return __num
    @classmethod
    def read_num(cls):
        return cls.__num

print(Teacher.read_num())  # 1 vccccccccc
解析

总结:  

  私有的 :通过过给__名字这样的属性或者方法加上当前所在类的前缀,把属性隐藏起来了
  只能在本类的内部使用,不能在类的外部使用,不能被继承
   property 把一个方法伪装成属性
   property和私有的两个概念一起用
   定义一个私有的
   再定义一个同名共有的方法,被property装饰
   @方法名.setter
   @方法名.deleter

2、classmethod 类方法

class Fruits:
    __discount = 0.8
    def __init__(self, name, price):
        print('init',self)
        self.name = name
        self.__price = price #给对象创建一个以类名为基础的变量,以便下次类调用

    @classmethod      # 把一个方法从对象方法,变成一个类方法
    def change_discount(cls,value):
        cls.__discount = value   # cls到底是谁??? Fruits

    @classmethod
    def get_discount(cls):
        return cls.__discount

print(Fruits.get_discount())  # 0.8
Fruits.change_discount(1)
print(Fruits.get_discount())  # 1

# 类方法
    # 1. 有些时候我们要修改的是类中的静态变量/类变量,此时根本不会和self有任何的操作关联
    # 2.这时传一个self参数对我们来说完全没有用, 我们希望接受的是当前我们所在的类
apple = Fruits('apple',8)
apple.change_discount(0.5)  # 会去找父类
print(Fruits.get_discount())  # 0.5
# 类方法推荐使用类名调用而不是使用对象名调用
解析

3、staticmethod 静态方法

class A:
    @staticmethod  # 声明这个方法只是一个普通的不会使用任何和这个类中的变量相关的方法
    def func():    # 此时 func是一个静态方法
        print('既不操作和self相关的内容')
        print('也不操作和类名相关的内容')
A.func()  #打印……

class Student:
    def __init__(self,name):
        self.name = name

    @staticmethod
    def login():
        pass
# 先获取这个学生的用户名和密码
# 判断他登录成功之后进行实例化
# Student.login()
# stu = Student('alex')
解析

 

二、反射

  反射: 通过字符串属性名 得到真正的这个字符串的名字对应的对象的属性值

class Student:
    def __init__(self,name):
        self.name = name

    def show_courses(self):
        print('调用了 show courses')

    def select_course(self):
        print('调用了 select course')

    def show_selected_course(self):
        print('调用了 show_selected_course')

    def quit(self):
        print('调用了 quit')
wu = Student('吴彪')
# 反射 通过字符串属性名 得到真正的这个字符串的名字对应的对象的属性值
ret = getattr(wu,'name')   # 内置函数 可以查看属性
print(ret)  # 吴彪
# print(getattr(wu,'show_courses'))  # wu.show_courses
getattr(wu,'show_courses')()  #调用了 show courses
getattr(wu,'select_course')()  #调用了 select course

*****
while True:
    name = input('user>>>')
    if hasattr(wu,name):
        func_attr = getattr(wu,name)  # 如果取出对象属性名
        if callable(func_attr):   #是否可以调用
            func_attr()
        else:
            print(func_attr)
解析

1、issubclass(Son,Foo) # 判断Son是否是Foo的子类,或是Son是否是Foo的子类,(类与类)

class Foo(object):pass
class Son(Foo):pass
ret = issubclass(Son,Foo)
print(ret)  #True
解析

2、isinstance(obj,cls) # 判断对象与类之间得关系,这个类也包括父类 (对象与类)

3、type() #和isinstance类似都是对象与类得关系,但是type只找实例化对象得类,而isinstance可以找所有祖宗

a = 1
ret1 = type(a) is int
ret2 = isinstance(a,int)
print(ret1) #True
print(ret2) #True
解析

4、

反射详解

  • 所有的a.b都可以变成 getattr(a,'b')

  • 用字符串数据类型的变量名'b' 来获取实际的变量值,实则用字符串数据类型的变量名 找到这个变量对应的内存地址

  • 作用于对象反射:obj.属性名 obj.方法名()

  • 作用于类反射: cls.静态变量名 cls.类方法名() cls.静态方法名()

  • 作用于模块反射: 模块名.方法名()

  • 作用于调用函数和方法: def func():pass

  • 作用于当前文件: 当前模块中得变量,函数等

    #简单的反射,在类中使用反射
    class Manager:   # 管理员用户
        def __init__(self,name):
            self.name  = name
        def create_course(self):  # 创建课程
            print('in Manager create_course')
    
        def create_student(self): # 给学生创建账号
            print('in Manager create_student')
    
        def show_courses(self): # 查看所有课程
            print('in Manager show_courses')
    
        def show_students(self): # 查看所有学生
            print('in Manager show_students')
    alex = Manager('alex')
    operate_lst = [('创建课程','create_course'),('创建学生账号','create_student'),
                   ('查看所有课程','show_courses'),('查看所有学生','show_students')]
    for index,opt in enumerate(operate_lst,1):
        print(index,opt[0])
    num = input('请输入您要做的操作 :')
    if num.isdigit():
        num = int(num)
        if hasattr(alex,operate_lst[num-1][1]):   #hasattr与getattr连用
            getattr(alex,operate_lst[num-1][1])()
            
     # 如何使用反射
    alex = Manager('alex')
    print(alex.name)    # ==> print(getattr(alex,'name'))   用的相对少
    funcname = 'create_course'
    a = getattr(alex,funcname)
    b = alex.create_course
    print(a)  
    print(b)  #a 和b一致 
    getattr(alex,'create_course')()   # ==> # alex.create_course()   用的多
    
    #类中的反射:
    class A:
        Country = '中国'
        @classmethod
        def show(cls):
            print('国家 : ',cls.Country)  #使用了类中的私有变量
    print(getattr(A,'Country'))   # print(A.Country)
    A.show  # getattr(A,'show')
    getattr(A,'show')()   # A.show()
    #类中反射两种方式:
        #对象名.属性名 / 对象名.方法名() 可以直接使用对象的方法和属性
        #当我们只有字符串数据类型的内容的时候
            # getattr(对象名,'方法名')()
            # getattr(对象名,'属性名')
            
    #反射模块中的方法
    import re
    ret = re.findall('\d+','2985urowhn0857023u9t4')
    print(ret)  #['2985', '0857023', '9', '4']
    'findall'
    getattr(re,'findall')   # re.findall
    ret = getattr(re,'findall')('\d','wuhfa0y80aujeiagu')
    print(ret)  #['0', '8', '0']
    
    import time
    time.time  == getattr(time,'time')
    time.time()  == getattr(time,'time')()
    
    # 反射本文件中的内容:只是要出现在全局变量中的名字,都可以通过getattr(modules[__name__],字符串数据类型的名字)获得
    from sys import modules
    print(modules)    # DICT KEY是模块的名字,value就是这个模块对应的文件地址
    
    import re
    print(re)   # <module 're' from 'D:\\python3\\lib\\re.py'>
    print(modules['re']) # 也就是找到re模块的内存地址
    print(modules['re'].findall) # print(re.findall)  参数添加在后面,效果相同,
    
    #打印本地加载的所有属性:
    a = 1
    b = 2
    print(__name__)  #__main__
    print('__main__')  #__main__
    print(getattr(modules[__name__],'a'))   #1
    
    #语法
    a = 1
    b = 2
    getattr(modules[__name__],'变量名')
    
    #函数名
    def func(a,b):
        print('in func',a,b)
    
    getattr(modules[__name__],'func')   # func
    getattr(modules[__name__],'func')(1,2)   # func
    
    #类名
    class Course:
        def func(self):
            print('in func')
    
    print(Course)
    print(getattr(modules[__name__],'Course'))   # Course
    getattr(modules[__name__],'Course')()   # 实例化的过程
    解析

总结:

hasattr和getattr:
   只要是a.b这种结构,都可以使用反射,用对象\类\模块反射,都只有以下场景
   这种结构有两种场景
     1、a.b b是属性或者变量值
        getattr(a,'b') == a.b
     2、a.b() b是函数或者方法
         a.b()
         getattr(a,'b')()
         a.b(arg1,arg2)
         getattr(a,'b')(arg1,arg2)
         a.b(*args,**kwargs)
         getattr(a,'b')(*args,**kwargs)
   如果是本文件中的内容,不符合a.b这种结构
     1、直接调用func()
       getattr(sys.modules[__name__],'func')()
     2、直接使用类名 Person()
        getattr(sys.modules[__name__],'Person')()
    3、直接使用变量名 print(a)
        getattr(sys.modules[__name__],'a')
    4、所有的getattr都应该和hasattr一起使用
         if hasattr():
          getattr()
   setattr 只用来修改或者添加属性\变量,不能用来处理函数或者是其他方法
       a.b = value
      setattr(a,'b',value)

 delattr 只用来删除 属性\变量
   del a.b 删除属性 相当于删除了a对象当中的b属性
   delattr(a,'b')

猜你喜欢

转载自www.cnblogs.com/double-W/p/10639575.html
今日推荐