operator - Interfaccia Funzionale agli Operatori Built-in

Scopo: Interfaccia funzionale agli operatori built-in.

Programmare utilizzando iteratori richiede occasionalmente la creazione di piccole funzioni per semplici espressioni. Talvolta queste possono essere implementate come funzioni lambda, ma per alcuni operatori nuove funzioni non sono assolutamente necessarie. Il modulo operator definisce funzioni che corrispondono a operazioni built-in per aritmetica, confronto e altre operazioni che corrispondono ad API per oggetti standard.

Operazioni logiche

Ci sono funzioni per determinare l'equivalente booleano di un valore, negarlo per creare il valore booleano opposto, e confrontare oggetti per vedere se sono identici.

# operator_boolean.py

from operator import *

a = -1
b = 5

print('a =', a)
print('b =', b)
print()

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

_not() comprende il carattere di sottolineatura all'inizio in quanto non è una parola chiave di Python. Per truth() si applica la stessa logica utilizzata quando si verifica una espressione in una istruzione if o quando si converte una espressione in un bool. is_() implementa la stessa verifica utilizzata dalla parola chiave is, mentre is_not() fa lo stesso controllo e ritorna la risposta opposta.

$ python3 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

Tutti gli operatori di confronto sono supportati.

# operator_comparisons.py

from operator import *

a = 1
b = 5.0

print('a =', a)
print('b =', b)
for func in (lt, le, eq, ne, ge, gt):
    print('{}(a, b): {}'.format(func.__name__, func(a, b)))

Le funzioni sono equivalenti alla sintassi delle espressioni che usano <, <=, ==, >= e >.

$ python3 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 supportati anche gli operatori aritmetici per la manipolazione di valori numerici.

# operator_math.py

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('\nPositivi/Negativi:')
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('\nArithmetici:')
print('add(a, b)     :', add(a, b))
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 separati: floordiv() (divisione di interi così come implementata da Python prima della versione 3.0) e truediv() (divisione a virgola mobile).

$ python3 operator_math.py

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

Positivi/Negativi:
abs(a): 1
neg(a): 1
neg(b): -5.0
pos(a): -1
pos(b): 5.0

Arithmetici:
add(a, b)     : 4.0
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 sequenze possono essere divisi in quattro gruppi: per la costruzione di sequenze, per la ricerca di elementi, per l'accesso ai contenuti e per la rimozione di elementi dalle sequenze.

# operator_sequences.py

from operator import *

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

print('a =', a)
print('b =', b)

print('\nContruttivi:')
print('  concat(a, b):', concat(a, b))

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 Elementi:')
print('  getitem(b, 1)                  :',
      getitem(b, 1))
print('  getitem(b, slice(1, 3))        :',
      getitem(b, slice(1, 3)))
print('  setitem(b, 1, "d")             :', end=' ')
setitem(b, 1, "d")
print(b)
print('  setitem(a, slice(1, 3), [4, 5]):', end=' ')
setitem(a, slice(1, 3), [4, 5])
print(a)

print('\nDistruttivi:')
print('  delitem(b, 1)          :', end=' ')
delitem(b, 1)
print(b)
print('  delitem(a, slice(1, 3)):', end=' ')
delitem(a, slice(1, 3))
print(a)

Alcune di queste operazioni, tipo setitem() e delitem(), modificano la sequenza sul posto e non ritornano un valore.

$ python3 operator_sequences.py

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

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

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

Accesso Elementi:
  getitem(b, 1)                  : b
  getitem(b, slice(1, 3))        : ['b', 'c']
  setitem(b, 1, "d")             : ['a', 'd', 'c']
  setitem(a, slice(1, 3), [4, 5]): [1, 4, 5]

Distruttivi:
  delitem(b, 1)          : ['a', 'c']
  delitem(a, slice(1, 3)): [1]

Operatori Sul Posto

Oltre agli operatori standard, molti tipi di oggetti supportano modifiche "sul posto" attraverso operatori speciali tipo +=. Ci sono delle funzioni equivalenti anche per queste modifiche.

# operator_inplace.py

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

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

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

Questi esempi dimostrano solo alcune di queste funzioni. Si faccia riferimento alla documentazione della libreria standard per completi dettagli.

$ python3 operator_inplace.py

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

a = iadd(a, b) => 4.0

c = iconcat(c, d) => [1, 2, 3, 'a', 'b', 'c']

Attributi e "Getter" di elementi

Una delle più inusuali caratteristiche del modulo operator è il concetto di getters. Questi sono oggetti chiamabili costruiti in fase di esecuzione per recuperare attributi di oggetti o contenuti da sequenze. I getters sono particolarmente utili quando si lavora con iteratori o generatori di sequenze, laddove sono intesi per avere meno overhead di una lambda o funzione Python.

# operator_attrgetter.py

from operator import *


class MyObj:
    """classe di esempio for attrgetter"""

    def __init__(self, arg):
        super().__init__()
        self.arg = arg

    def __repr__(self):
        return 'MyObj({})'.format(self.arg)


l = [MyObj(i) for i in range(5)]
print('oggetti   :', l)

# Estrae il valore 'arg' da ogni oggetto
g = attrgetter('arg')
vals = [g(i) for i in l]
print('valori arg:', vals)

# Sort using arg
l.reverse()
print('invertiti  :', l)
print('ordinati   :', sorted(l, key=g))

I getters di attributi lavorano come lambda x, n='nomeattributo': getattr(x, n).

$ python3 operator_attrgetter.py

oggetti   : [MyObj(0), MyObj(1), MyObj(2), MyObj(3), MyObj(4)]
valori arg: [0, 1, 2, 3, 4]
invertiti  : [MyObj(4), MyObj(3), MyObj(2), MyObj(1), MyObj(0)]
ordinati   : [MyObj(0), MyObj(1), MyObj(2), MyObj(3), MyObj(4)]

I getters di elementi lavorano come lambda x, n='nomeattributo': getattr(x, n).

# operator_itemgetter.py

from operator import *

l = [dict(val=-1 * i) for i in range(4)]
print('Dizionali:')
print(' originale:', l)
g = itemgetter('val')
vals = [g(i) for i in l]
print('   valori:', vals)
print('   ordinati:', sorted(l, key=g))

print
l = [(i, i * -2) for i in range(4)]
print('\nTuple:')
print(' originale:', l)
g = itemgetter(1)
vals = [g(i) for i in l]
print('   valori:', vals)
print('   ordinati:', sorted(l, key=g))

I getters di elementi lavorano sia con mappature che con sequenze.

$ python3 operator_itemgetter.py
Dizionali:
 originale: [{'val': 0}, {'val': -1}, {'val': -2}, {'val': -3}]
   valori: [0, -1, -2, -3]
   ordinati: [{'val': -3}, {'val': -2}, {'val': -1}, {'val': 0}]

Tuple:
 originale: [(0, 0), (1, -2), (2, -4), (3, -6)]
   valori: [0, -2, -4, -6]
   ordinati: [(3, -6), (2, -4), (1, -2), (0, 0)]

Combinare Operatori e Classi Personalizzate

Le funzioni nel modulo operator funzionano tramite le interfacce standard Python per le proprie operazioni, quindi funzionano con classi definite dall'utente alla stessa stregua dei tipi built-in.

# operator_classes.py

from operator import *


class MyObj:
    """Esempio per overload di operatore"""

    def __init__(self, val):
        super(MyObj, self).__init__()
        self.val = val

    def __str__(self):
        return 'MyObj({})'.format(self.val)

    def __lt__(self, other):
        """confronto per minore di"""
        print('Verifica {} < {}'.format(self, other))
        return self.val < other.val

    def __add__(self, other):
        """aggiunge valori"""
        print('Aggiungo {} + {}'.format(self, other))
        return MyObj(self.val + other.val)


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

print('Confronto:')
print(lt(a, b))

print('\nAritmetica:')
print(add(a, b))

Si faccia riferimento alla guida di riferimento di Python per un elenco completo dei metodi speciali utilizzati da ciascun operatore.

$ python3 operator_classes.py

Confronto:
Verifica MyObj(1) < MyObj(2)
True

Aritmetica:
Aggiungo MyObj(1) + MyObj(2)
MyObj(3)

Vedere anche:

operator
La documentazione della libreria standard per questo modulo.
functools
Strumenti per la programmazione funzionale, compreso il decoratore total_ordering per aggiungere metodi di confronto arricchito a una classe.
itertools
Operazioni di iterazione.
collections
Tipi astratti per collezioni
numbers
Tipi astratti per valori numerici.