operator
模块提供了一套与Python的内置运算符对应的高效率函数 . 许多函数名与特殊方法名相同,只是没有双下划线。为了向后兼容性,也保留了许多包含双下划线的函数。函数包含的种类有:对象的比较运算、逻辑运算、数学运算以及序列运算。
这个模块提供了很多和Python 一样的操作符, 这里 只是封装一个函数 进行调用
举个例子
import operator
b = operator.add(3,5)
print(b) # 8
operator 模块 封装了很多操作相关的函数, 比如 加,减 乘除 ,比较运算 ,逻辑运算 ,矩阵相乘. 还有一些封装好的类, 用起来 效率 比较高效. 本文 根据个人的使用经验 简单介绍一下, 这些类的常用用法.
表显示抽象操作如何对应于Python语法中的操作符号以及operator
模块中的函数。
操作 | 句法 | 功能 |
---|---|---|
加 | a + b |
add(a, b) |
串联 | seq1 + seq2 |
concat(seq1, seq2) |
成员测试 | obj in seq |
包含(seq, obj) |
除 | a / b |
truediv(a, b) |
整除 | a // b |
floordiv(a, b) |
按位与 | a & b |
and_(a, b) |
按位异或 | a ^ b |
xor(a, b) |
按位取反 | 〜 a |
invert(a) |
按位或 | a | b |
or_(a, b) |
取幂 | a ** b |
pow(a, b) |
身份运算 | a 是 b |
is_(a, b) |
身份运算 | a 是 不是 b |
is_not(a, b) |
索引分配 | obj [k] = v |
setitem(obj, k, v) |
索引删除 | del obj [k] |
delitem(obj, k) |
索引取值 | obj[k] |
getitem(obj, k) |
左移 | a b |
lshift(a, b) |
取模 | a % b |
mod(a, b) |
乘 | a * b |
mul(a, b) |
矩阵乘法 | a @ b |
matmul(a, b) |
取负 | - a |
neg(a) |
逻辑非 | 不是 a |
not_(a) |
取正 | + a |
pos(a) |
右移 | a >> b |
rshift(a, b) |
切片赋值 | seq [i:j] = 值 |
setitem(seq, slice(i, j), values) |
删除切片 | del seq [i:j] |
delitem(seq, slice(i, j)) |
切片 | seq[i:j] |
getitem(seq, slice(i, j)) |
取模(同%) | s % obj |
mod(s, obj) |
减 | a - b |
sub(a, b) |
真相测试 | obj |
truth(obj) |
小于 | a b |
lt(a, b) |
小于等于 | a b |
le(a, b) |
等于 | a == b |
eq(a, b) |
不等于 | a != b |
ne(a, b) |
大于等于 | a > = b |
ge(a, b) |
大于 | a > b |
gt(a, b) |
DESCRIPTION
This module exports a set of functions implemented in C corresponding
to the intrinsic operators of Python. For example, operator.add(x, y)
is equivalent to the expression x+y. The function names are those
used for special methods; variants without leading and trailing
'__' are also provided for convenience.
1 比较运算符
> >= < <= !=
对应函数的名称
gt ge lt le ne
2 逻辑运算符
与(&&) ,或(|) , 非(!), 异或 (^) , is , is not ,truth
对应函数的名称
and_ , or_ , not_ , xor
3 一些数学运算符 和一些 按位 运算符 bitwise Operations
+ - * / //(整除) %(取余) mod ,
matmul (矩阵相乘), mul
floordiv
index
mod
取绝对值 abs , pos , neg
inv ~ 取反
and_ 按位与
4 一些操作
contact contains
def concat(a, b):
"Same as a + b, for a and b sequences."
if not hasattr(a, '__getitem__'):
msg = "'%s' object can't be concatenated" % type(a).__name__
raise TypeError(msg)
return a + b
def contains(a, b):
"Same as b in a (note reversed operands)."
return b in a
def countOf(a, b):
"Return the number of times b occurs in a."
count = 0
for i in a:
if i == b:
count += 1
return count
def delitem(a, b):
"Same as del a[b]."
del a[b]
def getitem(a, b):
"Same as a[b]."
return a[b]
def indexOf(a, b):
"Return the first index of b in a."
for i, j in enumerate(a):
if j == b:
return i
else:
raise ValueError('sequence.index(x): x not in sequence')
def setitem(a, b, c):
"Same as a[b] = c."
a[b] = c
def length_hint(obj, default=0):
"""
Return an estimate of the number of items in obj.
This is useful for presizing containers when building from an iterable.
If the object supports len(), the result will be exact. Otherwise, it may
over- or under-estimate by an arbitrary amount. The result will be an
integer >= 0.
"""
if not isinstance(default, int):
msg = ("'%s' object cannot be interpreted as an integer" %
type(default).__name__)
raise TypeError(msg)
try:
return len(obj)
except TypeError:
pass
try:
hint = type(obj).__length_hint__
except AttributeError:
return default
try:
val = hint(obj)
except TypeError:
return default
if val is NotImplemented:
return default
if not isinstance(val, int):
msg = ('__length_hint__ must be integer, not %s' %
type(val).__name__)
raise TypeError(msg)
if val < 0:
msg = '__length_hint__() should return >= 0'
raise ValueError(msg)
return val
# In-place Operations *********************************************************
# 注意这些 并不能原地修改 值.,... 主要是Python ,函数 参数的传递方式,
# 我感觉是值 copy 一份到 , 函数里面. 所以 值并没有变. 可以看看例子.
def iadd(a, b):
"Same as a += b."
a += b
return a
def iand(a, b):
"Same as a &= b."
a &= b
return a
def iconcat(a, b):
"Same as a += b, for a and b sequences."
if not hasattr(a, '__getitem__'):
msg = "'%s' object can't be concatenated" % type(a).__name__
raise TypeError(msg)
a += b
return a
def ifloordiv(a, b):
"Same as a //= b."
a //= b
return a
def ilshift(a, b):
"Same as a <<= b."
a <<= b
return a
def imod(a, b):
"Same as a %= b."
a %= b
return a
def imul(a, b):
"Same as a *= b."
a *= b
return a
def imatmul(a, b):
"Same as a @= b."
a @= b
return a
def ior(a, b):
"Same as a |= b."
a |= b
return a
def ipow(a, b):
"Same as a **= b."
a **=b
return a
def irshift(a, b):
"Same as a >>= b."
a >>= b
return a
def isub(a, b):
"Same as a -= b."
a -= b
return a
def itruediv(a, b):
"Same as a /= b."
a /= b
return a
def ixor(a, b):
"Same as a ^= b."
a ^= b
return a
operator 中 有三个类, attrgetter , itemgetter ,methocaller 这三个类.
通过 attrgetter 可以 获取 对象的属性, 然后进行 排序 操作 .
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
@User : frank
@Time : 2019/7/29 22:53
@File : custom_sort.py
@Email : [email protected]
operator 中 可以使用 attrgetter 获取 对象的 属性
attrgetter('xxx') 来获取对象的属性 .
"""
from operator import attrgetter
class Student:
pass
def __init__(self, name, age, score):
self.name = name
self.age = age
self.score = score
def __str__(self):
return '%s(name=%r,age=%r,score=%r)' % (self.__class__.__name__, self.name, self.age, self.score)
__repr__ = __str__
if __name__ == '__main__':
std1 = Student("A", 11, 23)
std2 = Student("B", 13, 10)
std3 = Student("C", 16, 15)
std4 = Student("D", 34, 4)
students = [std1, std2, std3, std4]
# 按照分数 排序 , 升序
print("按照分数 排序,升序:")
students.sort(key=lambda student: student.score, reverse=False)
print(students)
# 按照 年龄排序 降序
print("按照 年龄排序 降序:")
print(sorted(students, key=attrgetter('age'), reverse=True))
# 按照 分数 排序
print("按照 分数 排序: ")
print(sorted(students, key=attrgetter('score'), reverse=False))
结果如下:
按照分数 排序,升序
[Student(name='D',age=34,score=4), Student(name='B',age=13,score=10), Student(name='C',age=16,score=15), Student(name='A',age=11,score=23)]
按照 年龄排序 降序:
[Student(name='D',age=34,score=4), Student(name='C',age=16,score=15), Student(name='B',age=13,score=10), Student(name='A',age=11,score=23)]
按照 分数 排序:
[Student(name='D',age=34,score=4), Student(name='B',age=13,score=10), Student(name='C',age=16,score=15), Student(name='A',age=11,score=23)]
from operator import attrgetter
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def __str__(self):
return 'Point({0},{1})'.format(self.x, self.y)
points = [Point(x, y) for x, y in zip([3, 2, 1, 0, 0.2], [0.1, -1, 5, 3, 1, 8])]
# 通过获取 points中 x 的值 进行排序.
points = sorted(points, key=attrgetter('x'))
for p in points:
print(p)
官方解释 :
Return a callable object that fetches item from its operand using the operand's __getitem__() method.
If multiple items are specified, returns a tuple of lookup values. For example:
After f = itemgetter(2), the call f(r) returns r[2].
After g = itemgetter(2, 5, 3), the call g(r) returns (r[2], r[5], r[3]).
这个 类会返回 一个 可调用对象 ,它会从 它的操作数里面取值,会调用 操作数的__getitem__() 方法 获取值.
如果 传入了多个 item , 那么 结果 就会返回一个元祖 .
返回使用操作数的__getitem__()
方法从其操作数获取项的可调用对象。如果指定了多个项,则返回查找值的元组。例如:
from operator import itemgetter
def one_example():
data = [
# (name , age)
("frank", 10),
("frank1", 15),
("frank2", 19),
("frank3", 12),
("frank4", 17),
("frank5", 11),
("frank6", 18),
]
data.sort(key=itemgetter(1), reverse=True)
print(data)
[('frank2', 19), ('frank6', 18), ('frank4', 17), ('frank1', 15), ('frank3', 12), ('frank5', 11), ('frank', 10)]
传入多个参数 可以获取多个值. 看下面的例子
itemgetter 里面 甚至 可以传入一个 slice 对象
>>> from operator import itemgetter
>>> itemgetter(1)('ABCDEFG')
'B'
>>> itemgetter(1, 3, 5)('ABCDEFG')
('B', 'D', 'F')
>>> itemgetter(slice(2, None))('ABCDEFG')
'CDEFG'
>>> inventory = [('apple', 3), ('banana', 2), ('pear', 5), ('orange', 1)]
>>> getcount = itemgetter(1)
>>> list(map(getcount, inventory))
[3, 2, 5, 1]
>>> getname = itemgetter(0)
>>> list(map(getname,inventory))
['apple', 'banana', 'pear', 'orange']
>>> sorted(inventory, key=getcount)
[('orange', 1), ('banana', 2), ('apple', 3), ('pear', 5)]
class methodcaller:
"""
Return a callable object that calls the given method on its operand.
After f = methodcaller('name'), the call f(r) returns r.name().
After g = methodcaller('name', 'date', foo=1), the call g(r) returns
r.name('date', foo=1).
"""
__slots__ = ('_name', '_args', '_kwargs')
def __init__(*args, **kwargs):
if len(args) < 2:
msg = "methodcaller needs at least one argument, the method name"
raise TypeError(msg)
self = args[0]
self._name = args[1]
if not isinstance(self._name, str):
raise TypeError('method name must be a string')
self._args = args[2:]
self._kwargs = kwargs
def __call__(self, obj):
return getattr(obj, self._name)(*self._args, **self._kwargs)
def __repr__(self):
args = [repr(self._name)]
args.extend(map(repr, self._args))
args.extend('%s=%r' % (k, v) for k, v in self._kwargs.items())
return '%s.%s(%s)' % (self.__class__.__module__,
self.__class__.__name__,
', '.join(args))
def __reduce__(self):
if not self._kwargs:
return self.__class__, (self._name,) + self._args
else:
from functools import partial
return partial(self.__class__, self._name, **self._kwargs), self._args
举个例子
operator.methodcaller(name[, args…])
参考文档 cook_book_method_caller 链接
有以下场景 :
你有一个字符串形式的方法名称,想通过它调用某个对象的对应方法。
import math
from operator import methodcaller
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def __str__(self):
return 'Point({0},{1})'.format(self.x, self.y)
def distance(self, x, y):
return math.hypot(self.x - x, self.y - y)
points = [Point(x, y) for x, y in zip([3, 2, 1, 0, 0.2], [0.1, -1, 5, 3, 1, 8])]
# 按离原点的距离排序
points.sort(key=methodcaller('distance', 0, 0))
for p in points:
print(p)
# operator.methodcaller() 创建一个可调用对象,并同时提供所有必要参数, 然后调用的时候只需要将实例对象传递给它即可,比如:
import math
from operator import methodcaller
p = Point(3, 4)
methodcaller('distance', 0, 0)
operator.methodcaller('distance', 0, 0)
d= methodcaller('distance', 0, 0)
d
operator.methodcaller('distance', 0, 0)
d(p)
5.0
测试 itemgetter , 和自己写 一个匿名函数之间排序 差异
时间的一个装饰器函数
from functools import wraps
import time
now = time.time
def fn_timer(fn=None, *, prefix=""):
"""
计算 fn 的运算时间
:param fn: 函数
:param prefix:
:return:
"""
if fn is None:
return partial(fn_timer, prefix=prefix)
@wraps(fn)
def function_timer(*args, **kwargs):
start = now()
result = fn(*args, **kwargs)
t = now() - start
if t > 0.002:
logger.info(f'{prefix}{fn.__name__} total running time {now() - start} seconds')
return result
return function_timer
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
@User : frank
@Time : 2019/7/30 11:16
@File : compare.py
@Email : [email protected]
"""
import random
import operator
from collections import defaultdict
from util.tools import fn_timer
from config.APP import LOG_PATH
from util.log import configure_logging
configure_logging(LOG_PATH)
def create_mobile():
prelist = ["130", "131", "132", "133", "134", "135", "136", "137", "138", "139",
"147", "150", "151", "152", "153", "155", "156", "157", "158", "159",
"186", "187", "188", "189"]
mobile = random.choice(prelist) + "".join(random.choice("0123456789") for _ in range(8))
return mobile
item = {
"peer_number": "13121111111",
"duration": 15,
"details_id": "xxxxxxxxxxxxx",
"dial_type": "DIALED",
"fee": 0,
"location": "上海",
"time": "2011-11-14 12:27:07",
"location_type": "国内被叫"
}
calls = []
for i in range(10_0000):
copy = item.copy()
copy['duration'] += random.randint(10, 60)
copy['mobile'] = create_mobile()
calls.append(copy)
# 记录手机号对应 通话时长
durations = defaultdict(int)
for call in calls:
mobile = call.get('mobile', "")
durations[mobile] = call.get('duration')
print(len(durations))
@fn_timer()
def sort_call(calls):
# 按照duration 排序
sorted_by_duration = sorted(calls, key=lambda e: e[1], reverse=True)
ret = sorted_by_duration[0:20]
return ret
@fn_timer()
def sort_call2(calls):
duration = operator.itemgetter(1)
# 按照duration 排序
sorted_by_duration = sorted(calls, key=duration, reverse=True)
ret = sorted_by_duration[0:20]
return ret
if __name__ == '__main__':
sort_call2(calls=durations)
sort_call(calls= durations)
pass
测试 排序花费的时间 .
数据量 | itemgetter 单位(s) | 匿名函数 单位(s) |
---|---|---|
100000 | 0.01939702033996582 | 0.023350000381469727 |
500000 | 0.10256004333496094 | 0.12110304832458496 |
100_0000 | 0.232496976852417 | 0.25639915466308594 |
从测试的性能可以看出 , itemgetter 还是要比匿名函数的效率要高一些. 对于一百万的数据的排序, 两百多毫秒 也就完成了 . 所以还是 库函数 的效率比自己写的效率要 高一些 呀.
本文总结 , 简单介绍了 operator 模块 的一些 函数的用法 ,以及类的用法. attrgetter , itemgetter , methodcaller 三个类 的基本使用 .
官方文档 中文版 https://docs.python.org/zh-cn/3/library/operator.html
官方文档 https://docs.python.org/3/library/operator.html
python cookbook 中 methodcaller 的使用 https://python3-cookbook.readthedocs.io/zh_CN/latest/c08/p20_call_method_on_object_by_string_name.html