python3中 operator模塊用法介紹


官方 operator 模塊

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)

1基本方法 介紹

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

2 三個類介紹

operator 中 有三個類, attrgetter , itemgetter ,methocaller 這三個類.

2-1 attrgetter 介紹

通過 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)
    

2-2 itemgetter 使用介紹

官方解釋 :

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__()方法從其操作數獲取的可調用對象。如果指定了多個項,則返回查找值的元組。例如:

1 用來排序
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)]

2 通過 獲取多個值

傳入多個參數 可以獲取多個值. 看下面的例子

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)]

2-3 methodcaller 介紹

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

3 性能測試

測試 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 還是要比匿名函數的效率要高一些. 對於一百萬的數據的排序, 兩百多毫秒 也就完成了 . 所以還是 庫函數 的效率比自己寫的效率要 高一些 呀.

4 總結

本文總結 , 簡單介紹了 operator 模塊 的一些 函數的用法 ,以及類的用法. attrgetter , itemgetter , methodcaller 三個類 的基本使用 .

5 參考文檔

官方文檔 中文版 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

分享快樂,留住感動. '2019-08-02 22:39:58' --frank
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章