operator - Interfaccia funzionale agli operatori built-in

Scopo Interfaccia funzionale agli operatori built-in.
Versione Python 1.4 e superiore

L'uso di iteratori nella programmazione funzionale richiede occasionalmente che si creino piccole funzioni per semplici espressioni. Talvolta esse possono essere espresse come funzioni lambda. Ma per alcune operazioni, non occorre definire le proprie funzioni. Il modulo operator definisce delle funzioni che corrispondono alle operazioni built-in per l'artitmetica, per il confronto così come per le operazioni su sequenze e dizionari.

Operazioni Logiche

Ci sono operazioni logiche per determinare l'equivalente booleano per un valore, per la negazione di esso per creare il valore opposto booleano, e per confrontare oggetti per vedere se sono identici.

from operator import *

a = -1
b = 5

print 'a =', a
print 'b =', b

print 'not_(a):', not_(a)
print 'truth(a):', truth(a)
print 'is_(a, b):', is_(a,b)
print 'is_not(a, b):', is_not(a,b)
$ python operator_boolean.py
a = -1
b = 5
not_(a): False
truth(a): True
is_(a, b): False
is_not(a, b): True

Operatori di Confronto

Sono supportati tuti gli operatori di confronto:

from operator import *

a = 1
b = 5.0

print 'a =', a
print 'b =', b
for func in (lt, le, eq, ne, ge, gt):
    print '%s(a, b):' % func.__name__, func(a, b)
$ python operator_comparisons.py
a = 1
b = 5.0
lt(a, b): True
le(a, b): True
eq(a, b): False
ne(a, b): True
ge(a, b): False
gt(a, b): False

Operatori Aritmetici

Sono anche supportati gli operatori aritmetici per la manipolazione di valori numerici

from operator import *

a = -1
b = 5.0
c = 2
d = 6

print 'a =', a
print 'b =', b
print 'c =', c
print 'd =', d

print '\nPositivo/Negativo:'
print 'abs(a):', abs(a)
print 'neg(a):', neg(a)
print 'neg(b):', neg(b)
print 'pos(a):', pos(a)
print 'pos(b):', pos(b)

print '\nArithmetica:'
print 'add(a, b):', add(a, b)
print 'div(a, b):', div(a, b)
print 'div(d, c):', div(d, c)
print 'floordiv(a, b):', floordiv(a, b)
print 'floordiv(d, c):', floordiv(d, c)
print 'mod(a, b):', mod(a, b)
print 'mul(a, b):', mul(a, b)
print 'pow(c, d):', pow(c, d)
print 'sub(b, a):', sub(b, a)
print 'truediv(a, b):', truediv(a, b)
print 'truediv(d, c):', truediv(d, c)

print '\nBitwise:'
print 'and_(c, d):', and_(c, d)
print 'invert(c):', invert(c)
print 'lshift(c, d):', lshift(c, d)
print 'or_(c, d):', or_(c, d)
print 'rshift(d, c):', rshift(d, c)
print 'xor(c, d):', xor(c, d)
Ci sono due operatori di divisione distinti. floordiv (divisione di interi ante 3.0) e truediv (divisione di valori a virgola mobile)
$ python operator_math.py
a = -1
b = 5.0
c = 2
d = 6

Positivo/Negativo:
abs(a): 1
neg(a): 1
neg(b): -5.0
pos(a): -1
pos(b): 5.0

Arithmetica:
add(a, b): 4.0
div(a, b): -0.2
div(d, c): 3
floordiv(a, b): -1.0
floordiv(d, c): 3
mod(a, b): 4.0
mul(a, b): -5.0
pow(c, d): 64
sub(b, a): 6.0
truediv(a, b): -0.2
truediv(d, c): 3.0

Bitwise:
and_(c, d): 2
invert(c): -3
lshift(c, d): 128
or_(c, d): 6
rshift(d, c): 1
xor(c, d): 4

Operatori per Sequenze

Gli operatori per lavorare con le sequenze possono essere divisi grosso modo in quatto gruppi: per comporre sequenze, per la ricerca, per lavorare con gli elementi, e per la rimozione di elementi dalle sequenze

from operator import *

a = [ 1, 2, 3 ]
b = [ 'a', 'b', 'c' ]

print 'a =', a
print 'b =', b

print '\nCostruzione:'
print 'concat(a, b):', concat(a, b)
print 'repeat(a, 3):', repeat(a, 3)

print '\nRicerca:'
print 'contains(a, 1):', contains(a, 1)
print 'contains(b, "d"):', contains(b, "d")
print 'countOf(a, 1):', countOf(a, 1)
print 'countOf(b, "d"):', countOf(b, "d")
print 'indexOf(a, 5):', indexOf(a, 1)

print '\nAccesso agli elementi:'
print 'getitem(b, 1):', getitem(b, 1)
print 'getslice(a, 1, 3)', getslice(a, 1, 3)
print 'setitem(b, 1, "d"):', setitem(b, 1, "d"), ',after b =', b
print 'setslice(a, 1, 3, [4, 5]):', setslice(a, 1, 3, [4, 5]), ', after a =', a

print '\nDistruzione:'
print 'delitem(b, 1):', delitem(b, 1), ',after b =', b
print 'delslice(a, 1, 3):', delslice(a, 1, 3), ', after a =', a
$ python operator_sequences.py
a = [1, 2, 3]
b = ['a', 'b', 'c']

Costruzione:
concat(a, b): [1, 2, 3, 'a', 'b', 'c']
repeat(a, 3): [1, 2, 3, 1, 2, 3, 1, 2, 3]

Ricerca:
contains(a, 1): True
contains(b, "d"): False
countOf(a, 1): 1
countOf(b, "d"): 0
indexOf(a, 5): 0

Accesso agli elementi:
getitem(b, 1): b
getslice(a, 1, 3) [2, 3]
setitem(b, 1, "d"): None ,after b = ['a', 'd', 'c']
setslice(a, 1, 3, [4, 5]): None , after a = [1, 4, 5]

Distruzione:
delitem(b, 1): None ,after b = ['a', 'c']
delslice(a, 1, 3): None , after a = [1]

Operatori "Sul Posto"

Oltre agli operatori standard, molti tipi di oggetto supportano la modifica "sul posto" attraverso operatori speciali come +=. Ci sono anche delle funzioni equivalenti per le modifiche "sul posto":

from operator import *

a = -1
b = 5.0
c = [ 1, 2, 3 ]
d = [ 'a', 'b', 'c']
print 'a =', a
print 'b =', b
print 'c =', c
print 'd =', d

print 'iadd(a, b):', iadd(a, b)
a = iadd(a, b)
print 'a = iadd(a, b) =>', a

print 'iconcat(c, d):', iconcat(c, d)
c = iconcat(c, d)
print 'c = iconcat(c, d) =>', c

Questi esempi dimostrano solo un paio di funzioni. Fare riferimento alla documentazione della libreria standard per il dettaglio completo.

$ python operator_inplace.py
a = -1
b = 5.0
c = [1, 2, 3]
d = ['a', 'b', 'c']
iadd(a, b): 4.0
a = iadd(a, b) => 4.0
iconcat(c, d): [1, 2, 3, 'a', 'b', 'c']
c = iconcat(c, d) => [1, 2, 3, 'a', 'b', 'c', 'a', 'b', 'c']

Attributi e "Getters" di Elementi

Una delle caratteristiche più inusuali del modulo operator è la nozione di getters. Essi sono oggetti chiamabili costruiti in fase di esecuzione per recuperare attributi di elementi dagli oggetti o dalle sequenze. I getters sono utili in particolar modo quando si lavora con iteratori o generatori di sequenze, laddove tendono a procurare un minore overhead rispetto ad una funzione lambda o Python.

I getters di attributi funzionano come lambda x, n='attrname': getattr(x, n):

from operator import *

class MyObj(object):
    """classe di esempio perattrgetter"""
    def __init__(self, arg):
        super(MyObj, self).__init__()
        self.arg = arg
    def __repr__(self):
        return 'MyObj(%s)' % self.arg

l = [ MyObj(i) for i in xrange(5) ]
print l
g = attrgetter('arg')
vals = [ g(i) for i in l ]
print vals
$ python operator_attrgetter.py
[MyObj(0), MyObj(1), MyObj(2), MyObj(3), MyObj(4)]
[0, 1, 2, 3, 4]

Mentre i getters di elementi funzionano come lambda x, y=5: x[y]:

from operator import *

print 'Dizionari:'
l = [ dict(val=i) for i in xrange(5) ]
print l
g = itemgetter('val')
vals = [ g(i) for i in l ]
print vals

print 'Tuple:'
l = [ (i, i*2) for i in xrange(5) ]
print l
g = itemgetter(1)
vals = [ g(i) for i in l ]
print vals

I getters di elementi funzionano anche con le mappature e le sequenze.

$ python operator_itemgetter.py
Dizionari:
[{'val': 0}, {'val': 1}, {'val': 2}, {'val': 3}, {'val': 4}]
[0, 1, 2, 3, 4]
Tuple:
[(0, 0), (1, 2), (2, 4), (3, 6), (4, 8)]
[0, 2, 4, 6, 8]

Lavorare Con Le Proprie Classi

Le funzioni nel modulo operator funzionano tramite le interfacce standard Python per le loro operazioni, quindi funzionano alla stessa stregua con le proprie classi ed i tipi built-in.

from operator import *

class MyObj(object):
    """Esempio per l'overloading dell'operatore"""
    def __init__(self, val):
        super(MyObj, self).__init__()
        self.val = val
        return
    def __str__(self):
        return 'MyObj(%s)' % self.val
    def __lt__(self, other):
        """confronto per minore-di"""
        print 'Test %s < %s' % (self, other)
        return self.val < other.val
    def __add__(self, other):
        """aggiunta valori"""
        print 'Aggiungo %s + %s' % (self, other)
        return MyObj(self.val + other.val)

a = MyObj(1)
b = MyObj(2)

print lt(a, b)
print add(a, b)
$ python operator_classes.py
Test MyObj(1) < MyObj(2)
True
Aggiungo MyObj(1) + MyObj(2)
MyObj(3)

Verifica del tipo

A parte gli operatori veri e propri, ci sono funzioni per verificare la conformità API per i tipi sequenza, numero e mappatura. I test non sono perfetti, visto che le interfacce non sono strettamente definite, ma danno qualche idea di ciò che è supportato.

from operator import *

class NoType(object):
   """Nessun supporto alle API di tipo"""

class MultiType(object):
    """Supporto alle API di tipo multiplo"""
    def __len__(self):
        return 0
    def __getitem__(self, name):
        return 'mappatura'
    def __int__(self):
        return 0

o = NoType()
t = MultiType()

for func in (isMappingType, isNumberType, isSequenceType):
    print '%s(o):' % func.__name__, func(o)
    print '%s(t):' % func.__name__, func(t)
$ python operator_typechecking.py
isMappingType(o): False
isMappingType(t): True
isNumberType(o): False
isNumberType(t): True
isSequenceType(o): False
isSequenceType(t): True

Vedere anche:

operator
La documentazione della libreria standard per questo modulo.