7Python全栈之路系列之面向对象运算符重载

Python全栈之路系列之面向对象运算符重载

运算符重载的概念如下:

  1. 运算符重载让类拦截常规的Python运算;
  2. 类可重载所有Python表达式运算符;
  3. 类也可重载打印、函数调用、属性点号运算等内置运算;
  4. 重载是类实例的行为想内置类型;
  5. 重载是通过提供特殊名称的类方法来实现的;

常见的运算符重载方法

方法 重载 调用
__init__ 构造函数 对象建立:X = Class(args)
__del__ 解析函数 X对象收回
__add__ 运算符+ 如果没有__iadd__,X+Y,X+=Y
__or__ 运算符或 如果没有__ior__
__repr__,__str__ 打印、转换 print(X)、repr(X)、str(X)
__call__ 函数调用 X(args, *kwargs)
__getattr__ 点号运算 X.undefined
__setattr__ 属性赋值语句 X.any = value
__delattr__ 属性删除 del X.any
__getattribute__ 属性获取 X.any
__getitem__ 索引运算 X[key],X[i:j],没__iter__时的for循环和其他迭代器
__setitem__ 索引赋值语句 X[key]=value,X[i:k]=sequence
__delitem__ 索引和分片删除 del X[key], del X[i:j]
__len__ 长度 len(X),如果没有__bool__,真值测试
__bool__ 布尔测试 bool(X),真测试
__lt____gt____le____ge____eq____ne__ 特定的比较 XY…,x>
__radd__ 右侧加法 Other + X
__iadd__ 增强的加法 X += Y
__iter____next__ 迭代环境 I=iter(X),next(I)
__contains__ 成员关系测试 item in X(任何可迭代对象)
__index__ 整数值 hex(X),bin(X),oct(X),o[X],O[X:]
__enter__,__exit__ 环境管理器 with obj as var:
__get__,__set__,__delete__ 描述符属性 X.attr,X.attr=Value,del X.attr
__new__ 创建 __init__之前创建对象

所有重载方法的名称前后都有两个下划线字符,以便把同类中定义的变量名区别开来。

构造函数和表达式:__init____sub__

>>> class Number:
...   def __init__(self, start):
...     self.data = start
...   def __sub__(self, other):
...     return Number(self.data - other)
... 
>>> X = Number(5)
>>> Y = X - 2
>>> Y
<__main__.Number object at 0x10224d550>
>>> Y.data
3

索引和分片: __getitem____setitem__

基本索引

>>> class Index:
...     def __getitem__(self, item):
...         return item ** 2
... 
>>> 
>>> for i in range(5):
...     I = Index()
...     print(I[i], end=‘ ‘)
... 
0 1 4 9 16

切片索引

>>> class Index:
...   data = [5, 6, 7, 8, 9]
...   def __getitem__(self, item):
...     print(‘getitem: ‘, item)
...     return self.data[item]
...   def __setitem__(self, key, value):
...     self.data[key] = value
... 
>>> X = Index()
>>> print(X[1:4])
getitem:  slice(1, 4, None)
[6, 7, 8] 
>>> X[1:4] = (1, 1, 1)
>>> print(X[1:4])
getitem:  slice(1, 4, None)
[1, 1, 1]

索引迭代:__getitem__

如果重载了这个方法,for循环每次循环时都会调用类的getitem方法;

>>> class stepper:
...     def __getitem__(self, item):
...         return self.data[item].upper()
... 
>>> 
>>> X = stepper()
>>> X.data = ‘ansheng‘
>>> for item in X:
...     print(item)
... 
A
N
S
H
E
N
G

迭代器对象:__iter____next__

>>> class Squares:
...   def __init__(self, start, stop):
...         self.value = start - 1
...         self.stop = stop
...   def __iter__(self):
...         return self
...   def __next__(self):
...         if self.value == self.stop:
...             raise StopIteration
...         self.value += 1
...         return self.value ** 2
... 
>>> for i in Squares(1, 5):
...   print(i)
... 
1
4
9
16
25

成员关系:__contains____iter____getitem__

class Iters:
    def __init__(self, value):
        self.data = value
        
    def __getitem__(self, item):
        print(‘get[%s]‘ % item, end=‘‘)
        return self.data[item]
        
    def __iter__(self):
        print(‘iter>==‘, end=‘‘)
        self.ix = 0
        return self
        
    def __next__(self):
        print(‘next:‘, end=‘‘)
        if self.ix == len(self.data): raise StopIteration
        item = self.data[self.ix]
        self.ix += 1
        return item
        
    def __contains__(self, item):
        print(‘contains: ‘, end=‘ ‘)
        return item in self.data
        
X = Iters([1, 2, 3, 4, 5])
print(3 in X)
for i in X:
    print(i, end=‘|‘)
    
print([i ** 2 for i in X])
print(list(map(bin, X)))

I = iter(X)
while True:
    try:
        print(next(I), end=‘ @‘)
    except StopIteration as e:
        break

属性引用:__getattr____setattr__

当通过未定义的属性名称和实例通过点号进行访问时,就会用属性名称作为字符串调用这个方法,但如果类使用了继承,并且在超类中可以找到这个属性,那么就不会触发。

>>> class empty:
...     def __getattr__(self, item):
...         if item == ‘age‘:
...             return 40
...         else:
...             raise AttributeError(item)
... 
>>> 
>>> x = empty()
>>> print(x.age)
40
>>> print(x.name)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 6, in __getattr__
AttributeError: name
>>> class accesscontrol:
...     def __setattr__(self, key, value):
...         if key == ‘age‘:
...             self.__dict__[key] = value
...         else:
...             raise AttributeError(key + ‘ not allowed‘)
... 
>>> 
>>> x = accesscontrol()
>>> x.age = 40
>>> print(x.age)
40
>>> x.name = ‘Hello‘
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 6, in __setattr__
AttributeError: name not allowed

__repr____str__会返回字符串表达式

__repr____str__都是为了更友好的显示,具体来说,如果在终端下print(Class)则会调用__repr__,非终端下会调用__str__方法,且这两个方法只能返回字符串;

class adder:
    def __init__(self, value=0):
        self.data = value
        
    def __add__(self, other):
        self.data += other
        
    def __repr__(self):
        return ‘addrepr(%s)‘ % self.data
        
    def __str__(self):
        return ‘N: %s‘ % self.data
        
x = adder(2)
x + 1
print(x)
print((str(x), repr(x)))

右侧加法和原处加法: __radd____iadd__

只有当+右侧的对象是类实例,而左边对象不是类实例的时候,Python才会调用__radd__

class Commuter:
    def __init__(self, val):
        self.val = val
        
    def __add__(self, other):
        print(‘add‘, self.val, other)
        return self.val + other
        
    def __radd__(self, other):
        print(‘radd‘, self.val, other)
        return other + self.val
        
x = Commuter(88)
y = Commuter(99)
print(x + 1)
print(‘‘)
print(1 + y)
print(‘‘)
print(x + y)

使用__iadd__进行原处加法

class Number:
    def __init__(self, val):
        self.val = val
    def __iadd__(self, other):
        self.val += other
        return self
        
x = Number(5)
x += 1
x += 1
print(x.val)

class Number:
    def __init__(self, val):
        self.val = val
        
    def __add__(self, other):
        return Number(self.val + other)
        
x = Number(5)
x += 1
x += 1
print(x.val)

Call表达式:__call__

当调用类实例时执行__call__方法

class Callee:
    def __call__(self, *args, **kwargs):
        print(‘Callee:‘, args, kwargs)
        
C = Callee()
C(1, 2, 3)
C(1, 2, 3, x=1, y=2, z=3)

class Prod:
    def __init__(self, value):
        self.value = value
        
    def __call__(self, other):
        return self.value * other
        
x = Prod(3)
print(x(3))
print(x(4))

比较:__lt__,__gt__和其他方法

类可以定义方法来捕获所有的6种比较运算符:<、>、<=、>=、==和!=

class C:
    data = ‘spam‘
    
    def __gt__(self, other):
        return self.data > other
        
    def __lt__(self, other):
        return self.data < other
        
x = C()
print(x > ‘han‘)
print(x < ‘han‘)

布尔值测试:boollen

class Truth:
    def __bool__(self):
        return True
        
X = Truth()
if X: print(‘yes‘)

class Truth:
    def __bool__(self):
        return False
        
X = Truth()
print(bool(X)

如果没有这个方法,Python退而求其次的求长度,因为一个非空对象看作是真:

>>> class Truth:
...   def __len__(self): return 0
... 
>>> X = Truth()
>>> if not X: print(‘no‘)
... 
no

如果两个方法都有,__bool__会胜过__len__

>>> class Truth:
...   def __bool__(self): return True
...   def __len__(self): return 0
... 
>>> X = Truth()
>>> bool(X)
True

如果两个方法都没有定义,对象毫无疑义的看作为真:

>>> class Truth: pass
... 
>>> bool(Truth)
True

对象解析函数:__del__

每当实例产生时,就会调用init构造函数,每当实例空间被收回时,它的对立面__del__,也就是解析函数,就会自动执行;

class Life:
    def __init__(self, name=‘unknown‘):
        print(‘Hello, ‘, name)
        self.name = name
        
    def __del__(self):
        print(‘Goodbye‘, self.name)
        
brian = Life(‘Brian‘)
brian = ‘loretta‘

#Python全栈之路 #运算符 #面向对象

时间: 2024-10-10 11:14:20

7Python全栈之路系列之面向对象运算符重载的相关文章

2Python全栈之路系列之面向对象进阶及类成员

Python全栈之路系列之面向对象进阶及类成员 再次了解多继承 先来一段代码 #!/usr/bin/env python # _*_ coding:utf-8 _*_ class A:     def bar(self):         print("BAR")         self.f1()          class B(A):     def f1(self):         print("B")          class C:     def 

7Python全栈之路系列之Django表单

Python全栈之路系列之Django表单 从Request对象中获取数据 HttpRequest对象包含当前请求URL的一些信息: 熟悉/方法 描述 例如 request.path 除域名以外的请求路径 /hello/ request.get_host() 访问的域名 127.0.0.1:8000" or www.example.com request.get_full_path() 请求路径,可能包含查询字符串 /hello/?print=true request.is_secure() 是

7Python全栈之路系列之协程

Python全栈之路系列之协程 What is the association? 与子例程一样,协程也是一种程序组件. 相对子例程而言,协程更为一般和灵活,但在实践中使用没有子例程那样广泛. 协程源自Simula和Modula-2语言,但也有其他语言支持. 协程更适合于用来实现彼此熟悉的程序组件,如合作式多任务,迭代器,无限列表和管道. 来自维基百科 https://zh.wikipedia.org/wiki/协程 协程拥有自己的寄存器上下文和栈,协程调度切换时,将寄存器上下文和栈保存到其他地方

Python全栈之路系列----之-----面向对象4接口与抽象,多继承与多态)

接口类与抽像类 在python中,并没有接口类这种东西,即便不通过专门的模块定义接口,我们也应该有一些基本的概念 编程思想 归一化设计: 1.接口类 不实现具体的方法,并且可以多继承 2.抽象类 可以做一些基础实现,并且不推荐多继承 编程的几类原则: 开放封闭原则:对扩展示开放的,对修改是封闭的依赖倒置原则:高层模块不应该依赖低层模块,二者都应该依赖其抽象:抽象不应该应该依赖细节:细节应该依赖抽象.换言之,要针对接口编程,而不是针对实现编程接口隔离原则:使用多个专门的接口,而不使用单一的总接口.

Python全栈之路系列----之-----面向对象

面向对象基本介绍 python编程方式: 面向过程编程  (流水线式思维) 优点是:极大的降低了写程序的复杂度,只需要顺着要执行的步骤,堆叠代码即可. 缺点是:一套流水线或者流程就是用来解决一个问题,代码牵一发而动全身. 面向函数编程 封装代码,简化流程 但是本质上没有改变结果   是面向结果编程思想 面向对象编程 优点是:解决了程序的扩展性.对某一个对象单独修改,会立刻反映到整个体系中,如对游戏中一个人物参数的特征和技能修改都很容易. 缺点:可控性差,无法向面向过程的程序设计流水线式的可以很精

1Python全栈之路系列之面向基础

Python全栈之路系列之面向对象基础 面向对象基本介绍 Python编程方式: 面向过程编程 面向函数编程 面向对象编程 名称定义: 如果函数没有在类中称之为函数 如果函数在类中称之为方法 创建类 # 创建一个类,类名是Class_basis class Class_basis:     # 在类里面创建了一个方法ret,类里面的方法必须加一个self关键字     def ret(self):         # 当调用方法的时候输出ret         print("ret")

3Python全栈之路系列之基于socket实现文件上传

Python全栈之路系列之基于socket实现文件上传 发布时间:2017年3月16日 00:04 浏览(106) 评论(0) 分类:Python 前言 此处没有前言 粘包 在实现发送文件功能之前我们先来理解下粘包的问题,下面有两张图,我觉得很清晰的就可以理解到了. 正常情况下发送文件 第一步: 客户端把获取到的文件总大小(size=65426)先放到缓冲区,然后发送给服务端 第二步: 此时客户端接收到的文件总大小就是65426 粘包的问题下发送文件 第一步: 客户端把获取到的文件总大小(siz

6Python全栈之路系列之MySQL存储过程

Python全栈之路系列之MySQL存储过程 存储过程是一个SQL语句集合,当主动去调用存储过程时,其中内部的SQL语句会按照逻辑执行. 存储过程过接收的参数 参数 描述 in 仅用于传入参数用 out 仅用于返回值用 inout 既可以传入又可以当作返回值 创建存储过程 创建一个简单的存储过程 -- 修改SQL语句的结束符为% delimiter % -- 创建这个存储过程先删除 DROP PROCEDURE IF EXISTS proc_p1 % CREATE PROCEDURE proc_

3Python全栈之路系列之MySQL表内操作

Python全栈之路系列之My SQL表内操作 先创创建一个表用于测试 -- 创建数据库 CREATE DATABASE dbname DEFAULT CHARSET utf8 COLLATE utf8_general_ci; -- 创建表 CREATE TABLE `tb` (   `id` int(5) NOT NULL AUTO_INCREMENT,   `name` char(15) NOT NULL,   `alias` varchar(10) DEFAULT NULL,   `ema