python高级编程 python 笔记3

"""  --*第三章 语法最佳实践--类级别以上*--  """
'''3.1 子类化内置类型'''
# object内置类型是所有内置类型的共同祖先
# 也是所有没有显式指定父类的用户自定义类的共同祖先
# 当需要实现与某个内置类型具有相似行为的类时,最好的方法是将这个内置类型子类化
# 定义一个类 是dict类型的子类 新类大部分行为与普通的dict相同
# 但不允许多个键对应相同的值 若重复会引起ValueError的子类错误
class DistinctError(ValueError):
    """若添加重复值,引发这个错误"""

class distinctdict(dict):
    """不接收重复值的字典"""
    def __setitem__(self, key, value):
        if value in self.values():
            if (
                    (key in self and self[key] != value) or
                key not in self
            ):
                raise DistinctError(
                    """this value already exist for different key"""
                )
        super().__setitem__(key, value)
my = distinctdict()
my['key'] = 'value'
print(my)  # {'key': 'value'}
# my['other_key'] = 'value'
# print(my)
# 报错
# in __setitem__
#     """this value already exist for different key"""
# __main__.DistinctError: this value already exist for different key

# 许多类都是对内置类型的部分实现 它们作为子类速度更快
# list类型用来管理序列,一个类需要在内部处理序列,可以对list进行子类化
class Folder(list):
    def __init__(self, name):
        self.name = name

    def dir(self, nesting=0):
        offset = " " * nesting
        print('%s%s/' % (offset, self.name))
        for element in self:
            if hasattr(element, 'dir'):
                element.dir(nesting + 1)
            else:
                print('%s %s' % (offset, element))

tree = Folder('project')
tree.append('README.md')
print(tree.dir())
# project/
#  README.md
src = Folder('src')
src.append('script.py')
tree.append(src)
print(tree.dir())
# project/
#  README.md
#  src/
#   script.py

'''3.2 访问超类中的方法'''
# super是一个内置类 可用于访问属于某个对象的超类的属性
class Mama:  # 旧的写法
    def says(self):
        print('do your homework')
class Sister(Mama):
    def says(self):
        Mama.says(self)  # 调用超类的says()方法,并将self作为参数传入
        print('and clean your bedroom')
print(Sister().says())
# 输出
# do your homework
# and clean your bedroom

# super()用法
class Sister(Mama):
    def says(self):
        super().says()
        print('and clean your room')
print(Sister().says())
# 输出同上
# super第二个参数可选,若只提供第一个参数,
# 那么super返回的是一个未绑定的类型
class Pizza:
    def __init__(self, toppings):
        self.toppings = toppings
    def __repr__(self):
        return "Pizza with " + " and ".join(self.toppings)
    @classmethod
    def recommend(cls):
        """推荐任意馅料(toppings)的某种披萨。"""
        return cls(['spam', 'ham', 'eggs'])
class VikingPizza(Pizza):
    @classmethod
    def recommend(cls):
        """推荐与super相同的内容,但多加了午餐肉(spam)。"""
        recommended = super(VikingPizza).recommend()
        recommended.toppings += ['spam'] * 5
        return recommended

# 使用super易犯的错误
# 1.混用super与显示类调用
class A:
    def __init__(self):
        print("A", end=" ")
        super().__init__()
class B:
    def __init__(self):
        print("B", end=" ")
        super().__init__()
class C(A, B):
    def __init__(self):
        print('C', end=" ")
        A.__init__(self)
        B.__init__(self)
print(C())
# 输出
# C A B B <__main__.C object at 0x0000000002769438>
# C类使用__init__方法调用他的基类,使得B类被调用两次
# C的实例调用了A.__init__(self),使得super(A,self).__init()调用了B.__init__()方法
# 2.不同种类的参数
# 使用super的另一个问题是初始化过程中参数传递
# 子类要与父类的签名相匹配

'''3.3 高级属性访问模式'''
# 1.描述符:允许你自定义在引用一个对象的属性时应该完成的事情
# 它是一个类,定义了另一个类的属性访问方式。一个类可以将属性委托给另一个类
# 描述符类基于3个特殊方法,这三个方法组成了描述符协议
# __set__:在设置属性时将调用这一方法,setter
# __get__:在读取属性时将调用这一方法,getter
# __delete__:对属性调用del时将调用这一方法
# 实现__get__()和__set__()的描述符被称为数据描述符
# 实现__get__()称为非数据描述符
# 属性查找时,这个协议的实际上由对象的特殊方法__getattribute__()调用。
# 每次通过点号或者getattr函数调用来执行这样的查找时,都会隐式的调用__getattribute__()
# 按下列顺序查找该属性
# [1]验证该属性是否为实例的类对象的描述符
# [2]如果不是,就查看该属性是否能在实例对象的__dict__中找到
# [3]最后,查看该属性是否为实例的类对象的非数据描述符
# 数据描述符优于__dict__查找,而__dict__查找优先于非数据描述符
class RevealAccess(object):
    """一个数据描述符,正常设定值并返回值,同时打印出记录访问信息
    """
    def __init__(self, initval=None, name='var'):
        self.val = initval
        self.name = name

    def __get__(self, obj, objtype):
        print('Retrieving', self.name)
        return self.val

    def __set__(self, obj, val):
        print('Updating', self.name)
        self.val = val

class MyClass(object):
    x = RevealAccess(10, 'var "x"')
    y = 5

m = MyClass()
print(m.x)  # 输出 Retrieving var "x"  10
m.x = 20  # 输出 Updating var "x"
# 若一个类的某个属性有数据描述符,那么每次查找这个属性时
# 都会调用描述符的__get__()方法并返回他的值
# 每次对这个属性赋值时都会调用__set__()
# 函数对象是非数据描述符
# 如果没有__dict__优先于非数据描述符,将不可能在运行时在已经构建好的实例上动态覆写特定的方法

# 现实例子-延迟求值属性
# 将类属性的初始化延迟到被实例访问时。
# 如果这些属性的初始化依赖全局应用上下文的话,这一点可能有用
# 另一使用场景是初始化代价很大,但在导入类的时候不知道是否会用到这个属性
class IninOnAccess:
    def __init__(self, klass, *args, **kwargs):
        self.klass = klass
        self.args = args
        self.kwargs = kwargs
        self._initialized = None
    def __get__(self, instance, owner):
        if self._initialized is None:
            print('initialized!')
            self._initialized = self.klass(*self.args,
                                           **self.kwargs)
        else:
            print('cached')
        return self._initialized
class MyClass:
    lazily_initialized = IninOnAccess(list, 'argument')
m= MyClass()
print(m.lazily_initialized)
# initialized!
# ['a', 'r', 'g', 'u', 'm', 'e', 'n', 't']
print(m.lazily_initialized)
# cached
# ['a', 'r', 'g', 'u', 'm', 'e', 'n', 't']

# 2.property:提供一个内置的描述符类型,将一个属性链接到一组方法上(把方法变成属性)
# 接受4个可选参数:fget、fset、fdel、doc。
# 最后一个参数可以用来定义一个链接到属性的docstring,就像一个方法一样。
# eg:控制方法两种,一种是直接访问保存两个顶点的属性,另一种是利用width和height
class Rectangle:
    def __init__(self, x1, y1, x2, y2):
        self.x1, self.y1 = x1, y1
        self.x2, self.y2 = x2, y2

    def _width_get(self):
        return self.x2 - self.x1

    def _width_set(self, value):
        self.x2 = self.x1 + value

    def _height_get(self):
        return self.y2 - self.y1

    def _height_set(self, value):
        self.y2 = self.y1 + value

    width = property(
        _width_get, _width_set,
        doc="rectangle width measured from left"
    )
    height = property(
        _height_get, _height_set,
        doc="rectangle height measured from top"
    )

    def __repr__(self):
        return "{}({}, {}, {}, {})".format(
            self.__class__.__name__,
            self.x1, self.y1, self.x2, self.y2
        )

rectangle = Rectangle(10, 10, 25, 34)
print(rectangle.width, rectangle.height)
print(rectangle)
# 在使用继承过程中必须要注意,创建的属性是利用当前类的方法实时创建
# 不会使用派生类中覆写的方法
# 例如:
class MetricRectangle(Rectangle):
    def _width_get(self):
        return "{} meters".format(self.x2 - self.x1)

print(Rectangle(0, 0, 100, 100).width)
# 输出
# 100
# 解决这一问题,要在派生类中覆写整个property
class MetricRectangle(Rectangle):
    def _width_get(self):
        return "{} meters".format(self.x2 - self.x1)

    width = property(_width_get, Rectangle.width.fset)

print(MetricRectangle(0, 0, 100, 100).width)
# 输出
# 100 meters
# 创建property最佳语法是使用property作为装饰器,减少类内部方法签名的数量
# 并提高代码的可读性和可维护性
class Rectangle:
    def __init__(self, x1, y1, x2, y2):
        self.x1, self.y1 = x1, y1
        self.x2, self.y2 = x2, y2
    @property
    def width(self):
        """rectangle height measured from left"""
        return self.x2 - self.x1
    @width.setter
    def width(self, value):
        self.x2 = self.x1 + value
    @property
    def height(self):
        """rectangle height measured from top"""
        return self.y2 - self.y1
    @height.setter
    def height(self, value):
        self.y2 = self.y1 + value
print(Rectangle(0, 0, 100, 100).width)
# 输出 100
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值