zoukankan      html  css  js  c++  java
  • 描述符及类的装饰器应用实现类型检查,自定制property,property补充,property实现类型检查

    '''描述符及类的装饰器应用'''
    # class Typed:
    #  '''这是一个数据描述符'''
    #  def __init__(self, one, two):
    #     self.one = one
    #     self.two = two
    #
    #  def __set__(self, instance, value): # 此步操作是People类实例化时或者单独给类设置值时才会触发
    #     if type(value) == self.two:
    #        instance.__dict__[self.one] = value
    #     else:
    #        raise TypeError('输入类型错误!')
    #
    #  def __get__(self, instance, owner): # ③然后执行的是__get__方法
    #     # return instance.__dict__[self.one]
    #     print('***')
    #
    #  def __delete__(self, instance):
    #     instance.__dict__.pop(self.one)
    #
    #
    # def deco(**kwargs):
    #  '''这是一个装饰器'''
    #  def wrapper(func):
    #     for key,val in kwargs.items():
    #        setattr(func, key, Typed(key, val)) # 设置类属性
    #     return func
    #  return wrapper
    #
    #
    # @deco(name=str, age=int, salary=float, gender=str, height=int)
    # class People:
    #  '''这是一个类,实现了自由定制类属性及实例化参数类型限制'''
    #  # name = Typed('name', str) # ②所以访问的是这一步
    #  # age = Typed('age', int)
    #  # salary = Typed('salary', float)
    #  # gender = Typed('gender', str)
    #  # height = Typed('height', int)
    #  def __init__(self, name, age, salary, gender, height):
    #     self.name = name
    #     self.age = age
    #     self.salary = salary
    #     self.gender = gender
    #     self.height = height
    
    # p1 = People('alex', 18, 1000.55, 'x', 170)
    # p1.name # ①数据描述符大于实例属性
    # print(People.__dict__)
    
    
    '''自定制property'''
    # class Lazyproperty:
    #  def __init__(self, func):
    #     # print(func) # <function Room.area at 0x000001C226872EE0>
    #     self.func = func
    #
    #  def __get__(self, instance, owner):
    #     if instance is None:
    #        return self # 当类调用时,返回实例本身(即一个内存地址)
    #     return self.func(instance)
    #
    # class Room:
    #  def __init__(self, name, width, length):
    #     self.name = name
    #     self.width = width
    #     self.length = length
    #
    #  @Lazyproperty # area = Lazyproperty(area),由类来作为装饰器,此步操作相当于实例化过程;相当于是给类设置类属性,所以可以让Lazyproperty写成描述符
    #  # @property # area = property(area)
    #  def area(self): # 由property装饰器装饰时:'area': <property object at 0x000001940DF045E0>
    #     return self.width * self.length
    #
    #  def func(self): # 'func': <function Room.func at 0x000001940DF02E50>
    #     return '...'
    
    # r1 = Room('客厅', 6, 7)
    # print(r1.area.func(r1)) # 没有__get__方法时,r1为Room实例,area由装饰器修饰后为Lazyproperty实例,所以有func,加上括号运行,参数为实例本身即r1
    # print(Room.__dict__)
    # print(r1.area)
    
    '''自我总结:这个装饰器不再是函数,而是一个类,且这个类属于描述符;在@的时候这一步相当于实例化,也等同于描述符设置类属性的过程;此时描述符有个__get__方法,
    instance是Room类的实例r1,owner是Room类,而self.func指向的是Room类的area属性地址,加括号即可运行;所以__get__方法等同于运行了Room类
    的area方法,而r1.area因为描述符的原因会去找Lazyproperty中的__get__方法'''
    
    # 当实例调用时,__get__方法的instance为实例本身;当类调用时,instance为None,所以自己这样的定制的装饰器会报错,而python提供的property
    # 则返回一个对象内存地址,那么加上一个if判断即可;此时类调用也就不会报错了
    # print(Room.area)
    
    
    '''自定制property实现延迟计算功能'''
    # class Lazyproperty1:
    #  def __init__(self, func):
    #     self.func = func
    #
    #  def __get__(self, instance, owner):
    #     # print(self) # <__main__.Lazyproperty1 object at 0x000001C6EE413D30>
    #     print('get')
    #     if instance is None:
    #        return self
    #     setattr(instance, self.func.__name__, self.func(instance)) # 设置实例字典属性,因为是非数据描述符,所以优先级的问题,实例属性有的,就不会去非数据描述符去找
    #     return self.func(instance)
    #
    # class Room1:
    #  def __init__(self, name, width, length):
    #     self.name = name
    #     self.width = width
    #     self.length = length
    #
    #  @Lazyproperty1
    #  def area(self):
    #     return int(self.width * self.length)
    #
    #  @property
    #  def area1(self):
    #     return int(self.width / self.length)
    #
    # r2 = Room1('卧室', 10, 10)
    # print(r2.area1)
    # print(r2.area) # 此时第一次运行会打印get
    # print(r2.__dict__) # {'name': '卧室', 'width': 10, 'length': 10, 'area': 100}
    # print(r2.area) # 第二次及往后的运行,就不会再打印get了,因为实例字典属性能找到,这样就不需要每次都运行函数进行计算了;如果描述符加了__set__成了数据描述符,结果又不一样了,因为优先级改变了
    
    
    '''property补充'''
    # class Foo:
    #  @property
    #  def ABC(self):
    #     print('get时候运行')
    #
    #  @ABC.setter
    #  def ABC(self, value):
    #     print('set时候运行', value)
    #
    #  @ABC.deleter
    #  def ABC(self):
    #     print('delete时候运行')
    
    # 只有在属性ABC定义property后才能定义ABC.setter和ABC.deleter
    # foo = Foo()
    # foo.ABC
    # foo.ABC = 'nnn' # 此时没有定义ABC.setter,报错AttributeError: can't set attribute
    # foo.ABC = 'mmm'
    # del foo.ABC
    
    
    # 另一种写法
    # class Foo1:
    #  def get_aaa(self):
    #     print('get')
    #
    #  def set_aaa(self, value):
    #     print('set')
    #
    #  def del_aaa(self):
    #     print('del')
    #
    #  aaa = property(get_aaa, set_aaa, del_aaa) # 括号里顺序不能变,必须是get,set,del
    #
    # f1 = Foo1()
    # f1.aaa
    # f1.aaa = 'bcbc'
    # del f1.aaa
    
    
    '''property应用'''
    # class Goods:
    #  def __init__(self):
    #     self.original_price = 100 # 原价
    #     self.discount = 0.8 # 折扣
    #
    #  @property
    #  def price(self):
    #     new_price = self.original_price * self.discount # 打完折扣后的加个
    #     return new_price
    #
    #  @price.setter
    #  def price(self, value): # 只能传入一个参数?
    #        self.original_price = value # 修改后的价格
    #
    #  @price.deleter
    #  def price(self):
    #     del self.original_price
    #
    # g1 = Goods()
    # print(g1.price) # 获取商品打完折扣后的价格80.0
    # g1.price = 200
    # print(g1.price) # 修改商品价格后的价格160.0
    # del g1.price
    # print(g1.price) # AttributeError: 'Goods' object has no attribute 'original_price'
    
    
    # property实现类型检查
    # class Typed:
    #  def __init__(self, name):
    #     self.name = name # 实例化就触发property
    #
    #  @property
    #  def name(self): # 这个类属性和实例属性同名了,而且加了property,此property属于数据描述符,所以优先级大于实例属性
    #     # return self.name # 如果只写一个property,property自动实现了set和get方法属于数据描述符,比实例属性优先级高,所以会触发property内置的set,抛出异常
    #     return self.doubi
    #
    #  @name.setter
    #  def name(self, value):
    #     if not isinstance(value, str): # 必须是字符串类型
    #        raise TypeError('输入类型错误')
    #     self.doubi = value
    #
    #  @name.deleter
    #  def name(self):
    #     del self.doubi
    #
    # t1 = Typed('alex')
    # t2 = Typed(111)
    while True: print('studying...')
  • 相关阅读:
    结对-五子棋游戏-开发过程
    团队-象棋游戏-设计文档
    课后作业-阅读任务-阅读提问-4
    团队-及格成绩查询系统-项目总结
    11.29-构建之法:现代软件工程-阅读笔记
    软件工程课程总结
    团队-及格成绩查询系统-最终程序
    团队-象棋游戏-团队一阶段互评
    课后作业-阅读任务-阅读提问-3
    课后作业-阅读任务-阅读笔记-3
  • 原文地址:https://www.cnblogs.com/xuewei95/p/14760816.html
Copyright © 2011-2022 走看看