Scopo | Interfaccia funzionale agli operatori built-in. |
Versione Python | 1.4 e superiore |
A partire dal 1 gennaio 2021 le versioni 2.x di Python non sono piu' supportate. Ti invito a consultare la corrispondente versione 3.x dell'articolo per il modulo operator
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.
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
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
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)
$ 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
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]
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']
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]
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)
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: