Python–cookbook–8.类与对象

Python–cookbook–8.类与对象

导入对应模块

import math
from abc import ABCMeta, abstractmethod
import collections
import bisect
import time
import weakref
import operator
from functools import total_ordering

改变对象的字符串显示

# __str__() 和 __repr__()

让对象支持上下文管理

# with -> (__enter__  __exit__)

创建大量对象时节省内存方法

# __slots__属性(固定大小数组而不是字典的内部表示)
class Date:
    __slots__ = ['year', 'month', 'day']
    def __init__(self, year, month, day):
        self.year = year
        self.month = month
        self.day = day

在类中封装属性名

# (单下划线和双下划线) 来命名私有属性

创建可管理的属性

# property

调用父类方法

# MRO(super())

子类中扩展property

# super()
class Person:
    def __init__(self, name):
        self.name = name
    @property
    def name(self):
        return self._name
    @name.setter
    def name(self, value):
        if not isinstance(value, str):
            raise TypeError('Expected a string')
        self._name = value
    @name.deleter
    def name(self):
        raise AttributeError("Can't delete attribute")

class SubPerson(Person):
    @property
    def name(self):
        print('Getting name')
        return super().name
    @name.setter
    def name(self, value):
        print('Setting name to', value)
        super(SubPerson, SubPerson).name.__set__(self, value)
    @name.deleter
    def name(self):
        print('Deleting name')
        super(SubPerson, SubPerson).name.__delete__(self)
# s = SubPerson('Guido')
# print(s.name)
# s.name = 'Larry'

创建新的类或实例属性

# class A:
#     pass
# print(A.__dict__)  # 是用来存储对象属性的一个字典,其键为属性名,值为属性的值
# print(dir(A))  # dir()函数会自动寻找一个对象的所有属性
# 一个描述器就是一个实现了三个核心的属性访问操作 (get, set, delete) 的类,
# 分别为 __get__() 、__set__() 和 __delete__() 这三个特殊的方法
class Integer:  # 描述器
    def __init__(self, name):
        self.name = name
    def __get__(self, instance, cls):
        #print('get被调用了')
        if instance is None:
            return self
        else:
            return instance.__dict__[self.name]
    def __set__(self, instance, value):
        #print('set被调用了')
        if not isinstance(value, int):
            raise TypeError('Expected an int')
        instance.__dict__[self.name] = value
    def __delete__(self, instance):
        #print('delete被调用了')
        del instance.__dict__[self.name]
class Point:
    x = Integer('x')  # 必须在类级别被定义
    y = Integer('y')
    def __init__(self, x, y):
        self.x = x
        self.y = y
# p = Point(2, 3)  # Point.x.__set__  Point.y.__set__
# print(p.x)  # Point.x.__get__
# print(Point.x)
# p.y = 5

使用延迟计算属性

# 访问的时候才会计算结果,结果值被缓存起来,不用每次都去计算
class lazyproperty:
    def __init__(self, func):
        self.func = func
    def __get__(self, instance, cls):
        if instance is None:
            return self
        else:
            value = self.func(instance)
            # 实例 函数 值
            setattr(instance, self.func.__name__, value)  # setattr(object, name, value)
            return value
# property实现
def lazyproperty2(func):
    name = '_lazy_' + func.__name__
    @property
    def lazy(self):
        if hasattr(self, name):
            return getattr(self, name)
        else:
            value = func(self)
            setattr(self, name, value)
            return value
    return lazy

class Circle:
    def __init__(self, radius):
        self.radius = radius
    @lazyproperty2
    def area(self):
        print('Computing area')
        return math.pi * self.radius ** 2
    @lazyproperty2
    def perimeter(self):
        print('Computing perimeter')
        return 2 * math.pi * self.radius
# c = Circle(4.0)
# print(c.radius)
# print(c.area)  # 首次计算
# print(c.area)

简化数据结构的初始化

# 仅仅用作数据结构的类,不想写太 __init__() 函数
# 在一个基类中写一个公用的 __init__() 函数
class Structure1:
    _fields = []
    def __init__(self, *args, **kwargs):
        if len(args) > len(self._fields):
            raise TypeError('Expected {} arguments'.format(len(self._fields)))
        for name, value in zip(self._fields, args):
            setattr(self, name, value)
        for name in self._fields[len(args):]:  # 支持关键字赋值
            setattr(self, name, kwargs.pop(name))
        if kwargs:
            raise TypeError('Invalid argument(s): {}'.format(','.join(kwargs)))
class Stock1(Structure1):
    _fields = ['name', 'shares', 'price']
class Point1(Structure1):
    _fields = ['x', 'y']
class Circle1(Structure1):
    _fields = ['radius']
    def area(self):
        return math.pi * self.radius ** 2
# s1 = Stock1('ACME', 50, 91.1)
# s2 = Stock1('ACME', 50, price=91.1)
# s3 = Stock1('ACME', shares=50, price=91.1)

定义接口或者抽象基类

class IStream(metaclass=ABCMeta):  # 抽象类
    @abstractmethod  # 抽象方法
    def read(self, maxbytes=-1):
        pass
    @abstractmethod
    def write(self, data):
        pass
# 除了继承这种方式外,还可以通过注册方式来让某个类实现抽象基类
# IStream.register(io.IOBase)

实现自定义容器

# collections 定义了很多抽象基类
# 例如实现迭代
class AIter(collections.abc.Iterable):
    pass
# 实现序列
class SortedItems(collections.abc.Sequence):
    def __init__(self, initial=None):
        self._items = sorted(initial) if initial is not None else []
    # Required sequence methods
    def __getitem__(self, index):
        return self._items[index]
    def __len__(self):
        return len(self._items)
    # Method for adding an item in the right location
    def add(self, item):
        bisect.insort(self._items, item)
# items = SortedItems([5, 1, 3])
# print(list(items))
# print(items[0], items[-1])
# items.add(2)
# print(list(items))
# print(isinstance(items, collections.abc.Iterable))

属性的代理访问:将某个操作转移给另外一个对象来实现

# 可以用来替代继承
class A:
    def spam(self, x):
        pass
    def foo(self):
        pass
class B1:
    """ 简单的代理 """
    def __init__(self):
        self._a = A()
    # def spam(self, x):
    #     return self._a.spam(x)
    def foo(self):
        return self._a.foo()
    def bar(self):
        pass
    def __getattr__(self, item):  # 如果需要大量方法需要代理
        print('__getattr__被调用')
        return getattr(self._a, item)
# b = B1()
# print(b.bar())
# print(b.spam(42))  # 对象不存在会调用__getattr__

代理模式

class Proxy:
    def __init__(self, obj):
        self._obj = obj
    def __getattr__(self, name):
        print('getattr:', name)
        return getattr(self._obj, name)
    def __setattr__(self, name, value):
        if name.startswith('_'):
            super().__setattr__(name, value)
        else:
            print('setattr:', name, value)
            setattr(self._obj, name, value)
    def __delattr__(self, name):
        if name.startswith('_'):
            super().__delattr__(name)
        else:
            print('delattr:', name)
            delattr(self._obj, name)
class Spam:
    def __init__(self, x):
        self.x = x
    def bar(self, y):
        print('Spam.bar:', self.x, y)
# s = Spam(2)
# p = Proxy(s)
# print(p.x)
# p.bar(3)
# p.x = 37

在类中定义多个构造器

class DateStructure:
    def __init__(self, year, month, day):
        self.year = year
        self.month = month
        self.day = day
    @classmethod
    def today(cls):  # 重新初始化,替代__init__
        print(cls)
        t = time.localtime()
        return cls(t.tm_year, t.tm_mon, t.tm_mday)
# a = DateStructure(2012, 12, 21) # Primary
# b = DateStructure.today()
# print(b.year, b.month, b.day)
class NewDate(DateStructure):
    pass
# c = DateStructure.today()  # Creates an instance of Date (cls=Date)
# d = NewDate.today()  # Creates an instance of NewDate (cls=NewDate)

创建不调用init方法的实例

class DateStructure2:
    def __init__(self, year, month, day):
        self.year = year
        self.month = month
        self.day = day
    @classmethod
    def today(cls):
        d = cls.__new__(cls)  # 使用__new__绕过__init__
        t = time.localtime()
        d.year = t.tm_year
        d.month = t.tm_mon
        d.day = t.tm_mday
        return d
# x = DateStructure2.today()
# print(x.year)

利用Mixins扩展类功能

# 你有很多有用的方法,想使用它们来扩展其他类的功能。但是这些类并没有任何继
# 承的关系。因此你不能简单的将这些方法放入一个基类,然后被其他类继承
class LoggedMappingMixin:
    __slots__ = () # 混入类都没有实例变量,因为直接实例化混入类没有任何意义
    def __getitem__(self, key):
        print('Getting ' + str(key))
        return super().__getitem__(key)
    def __setitem__(self, key, value):
        print('Setting {} = {!r}'.format(key, value))
        return super().__setitem__(key, value)
    def __delitem__(self, key):
        print('Deleting ' + str(key))
        return super().__delitem__(key)
class SetOnceMappingMixin:
    __slots__ = ()
    def __setitem__(self, key, value):
        if key in self:
            raise KeyError(str(key) + ' already set')
        return super().__setitem__(key, value)
class StringKeysMappingMixin:
    __slots__ = ()
    def __setitem__(self, key, value):
        if not isinstance(key, str):
            raise TypeError('keys must be strings')
        return super().__setitem__(key, value)
class LoggedDict(LoggedMappingMixin, dict):  # 通过多继承混入类
    pass
# d = LoggedDict()
# d['x'] = 23
# print(d['x'])
# del d['x']

实现状态对象或者状态机

# 每个状态定义一个对象
class ConnectionState:
    @staticmethod
    def read(conn):
        raise NotImplementedError()
    @staticmethod
    def write(conn, data):
        raise NotImplementedError()
    @staticmethod
    def open(conn):
        raise NotImplementedError()
    @staticmethod
    def close(conn):
        raise NotImplementedError()
class ClosedConnectionState(ConnectionState):
    @staticmethod
    def read(conn):
        raise RuntimeError('Not open')
    @staticmethod
    def write(conn, data):
        raise RuntimeError('Not open')
    @staticmethod
    def open(conn):
        conn.new_state(OpenConnectionState)
    @staticmethod
    def close(conn):
        raise RuntimeError('Already closed')
class OpenConnectionState(ConnectionState):
    @staticmethod
    def read(conn):
        print('reading')
    @staticmethod
    def write(conn, data):
        print('writing')
    @staticmethod
    def open(conn):
        raise RuntimeError('Already open')
    @staticmethod
    def close(conn):
        conn.new_state(ClosedConnectionState)
class Connection1:
    def __init__(self):
        self.new_state(ClosedConnectionState)
    def new_state(self, newstate):
        self._state = newstate
    def read(self):
        return self._state.read(self)
    def write(self, data):
        return self._state.write(self, data)
    def open(self):
        return self._state.open(self)
    def close(self):
        return self._state.close(self)
# cx = Connection1()
# print(cx._state)
# cx.open()
# print(cx._state)
# cx.read()
# cx.write('hello')
# cx.close()
# print(cx._state)

通过字符串调用对象方法

# 你有一个字符串形式的方法名称,想通过它调用某个对象的对应方法
class PointX:
    def __init__(self, x, y):
        self.x = x
        self.y = y
    def __repr__(self):
        return 'Point({!r:},{!r:})'.format(self.x, self.y)
    def distance(self, x, y):
        return math.hypot(self.x - x, self.y - y)
# p = PointX(2, 3)
# print(getattr(p, 'distance')(0, 0))  # 相当于调用 p.distance(0, 0)
# # operator.methodcaller() 创建一个可调用对象,并同时提供所有必要参数,然
# # 后调用的时候只需要将实例对象传递给它即可
# print(operator.methodcaller('distance', 0, 0)(p))

实现访问者模式

class Node:
    pass
class UnaryOperator(Node):
    def __init__(self, operand):
        self.operand = operand
class BinaryOperator(Node):
    def __init__(self, left, right):
        self.left = left
        self.right = right
class Add(BinaryOperator):
    pass
class Sub(BinaryOperator):
    pass
class Mul(BinaryOperator):
    pass
class Div(BinaryOperator):
    pass
class Negate(UnaryOperator):
    pass
class Number(Node):
    def __init__(self, value):
        self.value = value
class NodeVisitor:
    def visit(self, node):
        methname = 'visit_' + type(node).__name__
        meth = getattr(self, methname, None)
        if meth is None:
            meth = self.generic_visit
        return meth(node)
    def generic_visit(self, node):
        raise RuntimeError('No {} method'.format('visit_' + type(node).__name__))
class Evaluator(NodeVisitor):
    def visit_Number(self, node):
        return node.value
    def visit_Add(self, node):
        return self.visit(node.left) + self.visit(node.right)
    def visit_Sub(self, node):
        return self.visit(node.left) - self.visit(node.right)
    def visit_Mul(self, node):
        return self.visit(node.left) * self.visit(node.right)
    def visit_Div(self, node):
        return self.visit(node.left) / self.visit(node.right)
    def visit_Negate(self, node):
        return -node.operand
# t1 = Sub(Number(3), Number(4))
# t2 = Mul(Number(2), t1)
# t3 = Div(t2, Number(5))
# t4 = Add(Number(1), t3)
# e = Evaluator()
# print(e.visit(t4))
# 不用递归实现访问者模式
class Evaluator2(NodeVisitor):
    def visit_Number(self, node):
        return node.value
    def visit_Add(self, node):
        yield (yield node.left) + (yield node.right)
    def visit_Sub(self, node):
        yield (yield node.left) - (yield node.right)
    def visit_Mul(self, node):
        yield (yield node.left) * (yield node.right)
    def visit_Div(self, node):
        yield (yield node.left) / (yield node.right)
    def visit_Negate(self, node):
        yield - (yield node.operand)

循环引用数据结构的内存管理

class NodeX:
    def __init__(self, value):
        self.value = value
        self._parent = None
        self.children = []
    def __repr__(self):
        return 'Node({!r:})'.format(self.value)
    # property that manages the parent as a weak-reference
    @property
    def parent(self):
        return None if self._parent is None else self._parent()
    @parent.setter
    def parent(self, node):
        # 弱引用就是一个对象指针,它不会增加它的引用计数弱引用就是一个对象指针
        # 它不会增加它的引用计数
        self._parent = weakref.ref(node)
    def add_child(self, child):
        self.children.append(child)
        child.parent = self
# root = NodeX('parent')
# c1 = NodeX('child')
# root.add_child(c1)
# print(c1.parent)
# del root
# print(c1.parent)

让类支持比较操作

# 装饰器 functools.total_ordering
# 你只需定义一个 __eq__() 方法,外加其他方法 (__lt__, __le__, __gt__, or
# __ge__) 中的一个即可。然后装饰器会自动为你填充其它比较方法
class Room:
    def __init__(self, name, length, width):
        self.name = name
        self.length = length
        self.width = width
        self.square_feet = self.length * self.width
@total_ordering
class House:
    def __init__(self, name, style):
        self.name = name
        self.style = style
        self.rooms = list()
    @property
    def living_space_footage(self):
        return sum(r.square_feet for r in self.rooms)
    def add_room(self, room):
        self.rooms.append(room)
    def __str__(self):
        return '{}: {} square foot {}'.format(self.name,
                self.living_space_footage,
                self.style)
    def __eq__(self, other):
        return self.living_space_footage == other.living_space_footage
    def __lt__(self, other):
        return self.living_space_footage < other.living_space_footage
# h1 = House('h1', 'Cape')
# h1.add_room(Room('Master Bedroom', 14, 21))
# h1.add_room(Room('Living Room', 18, 20))
# h1.add_room(Room('Kitchen', 12, 16))
# h1.add_room(Room('Office', 12, 12))
# h2 = House('h2', 'Ranch')
# h2.add_room(Room('Master Bedroom', 14, 21))
# h2.add_room(Room('Living Room', 18, 20))
# h2.add_room(Room('Kitchen', 12, 16))
# h3 = House('h3', 'Split')
# h3.add_room(Room('Master Bedroom', 14, 21))
# h3.add_room(Room('Living Room', 18, 20))
# h3.add_room(Room('Office', 12, 16))
# h3.add_room(Room('Kitchen', 15, 17))
# houses = [h1, h2, h3]
# print('Is h1 bigger than h2?', h1 > h2) # prints True
# print('Is h2 smaller than h3?', h2 < h3) # prints True
# print('Is h2 greater than or equal to h1?', h2 >= h1) # Prints False
# print('Which one is biggest?', max(houses)) # Prints 'h3: 1101-square-foot-Split
# print('Which is smallest?', min(houses)) # Prints 'h2: 846-square-foot Ranch

创建缓存实例

# 在创建一个类的对象时,如果之前使用同样参数创建过这个对象,你想返回它的缓存引用
class SpamX:
    def __init__(self, name):
        self.name = name
_spam_cache = weakref.WeakValueDictionary()
def get_spam(name):
    if name not in _spam_cache:
        s = SpamX(name)
        _spam_cache[name] = s
    else:
        s = _spam_cache[name]
    return s
# a = get_spam('foo')
# b = get_spam('bar')
# c = get_spam('foo')
# print(a is b)
# print(a is c)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

柴寺仓

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值