Python 类对象

Python 类对象

经典迭代器

  • 可迭代对象的定义: 使用内置的iter可以获取迭代器的对象。如果对象实现了能返回迭代器的__iter__方法,那么对象就是可迭代的。序列都可以迭代。实现了__getitem__方法,而且接受从0开始的索引,这种对象也是可以迭代的。

  • 可迭代对象与迭代器之间的关系:Python从可迭代对象中获取迭代器。

import re
import reprlib

RE_WORD = re.compile(r'\w+')


class SentenceV2:

    def __init__(self, text):
        self.text = text
        self.words = RE_WORD.findall(text)

    def __repr__(self):
        return f'Sentence({reprlib.repr(self.text)})'

    def __iter__(self):
        # 返回一个迭代器
        return SentenceIterator(self.words)


class SentenceIterator:

    def __init__(self, words):
        self.words = words  
        # 初始化索引
        self.index = 0

    def __next__(self):
        try:
            word = self.words[self.index]
        except IndexError:
            raise StopIteration()
        self.index += 1
        return word

    def __iter__(self):  
        return self
  • 可迭代对象有一个__iter__方法,每次都实例化一个新迭代器。
  • 迭代器要实现__next__方法,返回单个元素,此外还要实现__iter__方法,返回迭代器本身。
  • 迭代器也是可迭代对象,但是可迭代对象不是迭代器。
生成器函数
import re
import reprlib

RE_WORD = re.compile(r'\w+')


class SentenceV3:

    def __init__(self, text):
        self.text = text
        self.words = RE_WORD.findall(text)

    def __repr__(self):
        return 'Sentence(%s)' % reprlib.repr(self.text)

    def __iter__(self):
        for word in self.words:
            # 产生当前的word
            yield word
            
            
class ArithmeticProgression:

    def __init__(self, begin, step, end=None):
        self.begin = begin
        self.step = step
        self.end = end  # None -> "infinite" series

    def __iter__(self):
        result_type = type(self.begin + self.step)
        result = result_type(self.begin)
        forever = self.end is None
        while forever or result < self.end:
            yield result
            result += self.step

只要Python函数的主体中有yield关键字,该函数就是生成器函数。调用生成器函数,返回一个生成器对象。

生成器工作原理:

  1. 生成器函数创建一个生成器对象,包装生成器函数的主体。
  2. 把生成器对象传给next()函数时,生成器函数提前执行函数主体中的下一个yield语句,返回产出的值,并在函数主体的当前位置暂停。
  3. 函数的主体返回时,Python创建的外层生成器对象抛出StopIteration异常。

上下文管理器

import sys

class LookingGlass:

    def __enter__(self):
        self.original_write = sys.stdout.write
        # 打上猴子补丁
        sys.stdout.write = self.reverse_write
        return 'JABBERWOCKY'

    def reverse_write(self, text):
        # 反转参数的内容
        self.original_write(text[::-1])

    def __exit__(self, exc_type, exc_value, traceback):
        # 将原来的方法还原
        sys.stdout.write = self.original_write
        if exc_type is ZeroDivisionError:
            print('Please DO NOT divide by zero!')
            return True

image-20240624090216546

Vector2d

from array import array 
import math 
 
class Vector2d: 
    typecode = 'd'  
    def __init__(self, x, y): 
        self.x = float(x)    
        self.y = float(y) 
        
 	# 可以解包 v = Vector2d(2,2)
    # x,y = v
    def __iter__(self): 
        return (i for i in (self.x, self.y))  
 	# print返回
    def __repr__(self): 
        class_name = type(self).__name__ 
        return '{}({!r}, {!r})'.format(class_name, *self)  
 
    def __str__(self): 
        # 调用__iter__
        return str(tuple(self))  
 
    def __bytes__(self): 
        return (bytes([ord(self.typecode)]) +  
                bytes(array(self.typecode, self)))  
 	# 判断Vector2d是否相等
    def __eq__(self, other): 
        return tuple(self) == tuple(other) 
    def __abs__(self): 
        return math.hypot(self.x, self.y)
    def __bool__(self): 
        return bool(abs(self))  

格式化显示

>>> format(42, 'b') 
'101010' 
>>> format(2 / 3, '.1%') 
'66.7%'
# datetime类重构了__format__方法
>>> from datetime import datetime 
>>> now = datetime.now() 
>>> format(now, '%H:%M:%S') 
'18:49:05' 
>>> "It's now {:%I:%M %p}".format(now) 
"It's now 06:49 PM"
 def __format__(self, fmt_spec=''): 
        components = (format(c, fmt_spec) for c in self)  
        return '({}, {})'.format(*components)  
>>> v1 = Vector2d(3, 4) 
>>> format(v1) 
'(3.0, 4.0)' 
>>> format(v1, '.2f') 
'(3.00, 4.00)' 
>>> format(v1, '.3e') 
'(3.000e+00, 4.000e+00)'

可哈希的

为了把 Vector2d 实例变成可哈希的,必须实现 hash 方法 (还需要 eq 方法,前面已经实现了)。此外,还要让向量实例 不可变

class Vector2d: 
    typecode = 'd' 
 
    def __init__(self, x, y): 
        self.__x = float(x)  
        self.__y = float(y) 
 
    @property  
    def x(self): 
        return self.__x 
 
    @property  
    def y(self): 
        return self.__y 

     def __hash__(self): 
        return hash((self.x, self.y))

使用 slots 节省空间

默认情况下,Python 把各个实例的属性存储在一个名为 dict 的字典中字典消耗的内存很多。但是,如果定义一个名为 slots 的类属性,以序列的形式 存储属性名称,那么 Python 将使用其他模型存储实例属性: slots 中的属性名称存储在一个隐藏的引用数组中,消耗的内 存比字典少。

>>> class Pixel: 
...     __slots__ = ('x', 'y')  
... 
>>> p = Pixel()  
>>> p.__dict__  
Traceback (most recent call last): 
  ... 
AttributeError: 'Pixel' object has no attribute '__dict__' 
>>> p.x = 10 
>>> p.y = 20 
# 不允许添加其他属性
>>> p.color = 'red' 
Traceback (most recent call last): 
  ... 
AttributeError: 'Pixel' object has no attribute 'color'

Vector

多维向量

>>> Vector([3.1, 4.2]) 
Vector([3.1, 4.2]) 
>>> Vector((3, 4, 5)) 
Vector([3.0, 4.0, 5.0]) 
>>> Vector(range(10)) 
Vector([0.0, 1.0, 2.0, 3.0, 4.0, ...])
from array import array 
import reprlib 
import math 
 
 
class Vector: 
    typecode = 'd' 
 
    def __init__(self, components): 
        self._components = array(self.typecode, components) 
 
    def __iter__(self): 
        return iter(self._components) 
 
    def __repr__(self): 
        # 替换为省略号
        # 返回array('d', [0.0, 1.0, 2.0, 3.0, 4.0, ...])
        components = reprlib.repr(self._components)  
        components = components[components.find('['):-1]  
        return f'Vector({components})' 
 
    def __str__(self): 
        return str(tuple(self)) 
 
    def __bytes__(self): 
        return (bytes([ord(self.typecode)]) + 
                bytes(self._components)) 
    # 调用len()返回
    def __len__(self): 
        return len(self._components) 

     def __eq__(self, other): 
        if len(self) != len(other): 
           return False 
        for a, b in zip(self, other): 
            if a != b:  
                return False 
        return True  
 
    def __abs__(self): 
        return math.hypot(*self) 
 
    def __bool__(self): 
        return bool(abs(self))

切片

    def __getitem__(self, index): 
        return self._components[index]
>>> v1 = Vector([3, 4, 5]) 
>>> len(v1) 
3 
>>> v1[0], v1[-1] 
(3.0, 5.0) 
>>> v7 = Vector(range(7)) 
# 返回的是array
>>> v7[1:4] 
array('d', [1.0, 2.0, 3.0])
def __getitem__(self, key): 
    	# 调用Vector[a:b:c] 传入的是slice(a,b,c)
        if isinstance(key, slice): 
            cls = type(self)  

            return cls(self._components[key]) 
        # 调用Vector[a]
        index = operator.index(key)  
        return self._components[index]  

动态存取属性

# 希望xyzt能获取前4个元素
>>> v = Vector(range(10)) 
>>> v.x 
0.0 
>>> v.y, v.z, v.t 
(1.0, 2.0, 3.0)
 __match_args__ = ('x', 'y', 'z', 't')  
 
    def __getattr__(self, name): 
        cls = type(self)  
        try: 
            pos = cls.__match_args__.index(name)  
        except ValueError: 
            pos = -1 
        if 0 <= pos < len(self._components): 
            return self._components[pos] 
        msg = f'{cls.__name__!r} object has no attribute {name!r}'  

        raise AttributeError(msg)
>>> a = Vector(range(9))
>>> a
Vector([0.0, 1.0, 2.0, 3.0, 4.0, ...])
>>> a.x
0.0
>>> a.y
1.0
# x不应该可以直接复制,这样会创建一个x变量
>>> a.x = 10
>>> a
Vector([0.0, 1.0, 2.0, 3.0, 4.0, ...])
>>> a.x
10
>>> 
 # 赋值操作
    def __setattr__(self, name, value): 
        cls = type(self) 
        if len(name) == 1:  

            if name in cls.__match_args__:  
                error = 'readonly attribute {attr_name!r}' 
            elif name.islower():  

                error = "can't set attributes 'a' to 'z' in 
{cls_name!r}" 
            else: 
                error = ''  
            if error:  


                msg = error.format(cls_name=cls.__name__, 
attr_name=name) 
                raise AttributeError(msg) 
        # 没问题就调用父类方法
        super().__setattr__(name, value)  

哈希

 def __hash__(self): 
        hashes = (hash(x) for x in self._components) 
        # reduce类似于递归函数,sum、any 和 all的本质都是调用reduce
        return functools.reduce(operator.xor, hashes, 0) 
     msg = error.format(cls_name=cls.__name__, 

attr_name=name)
raise AttributeError(msg)
# 没问题就调用父类方法
super().setattr(name, value)


### 哈希

```python
 def __hash__(self): 
        hashes = (hash(x) for x in self._components) 
        # reduce类似于递归函数,sum、any 和 all的本质都是调用reduce
        return functools.reduce(operator.xor, hashes, 0) 
  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值