2015-05-27 18 views
12

Sono a conoscenza della creazione di un metodo personalizzato __repr__ o __add__ (e così via), per modificare il comportamento di operatori e funzioni. Esiste un metodo di sostituzione per len?È possibile modificare il comportamento di len()?

Ad esempio:

class Foo: 
    def __repr__(self): 
     return "A wild Foo Class in its natural habitat." 

foo = Foo() 

print(foo)   # A wild Foo Class in its natural habitat. 
print(repr(foo)) # A wild Foo Class in its natural habitat. 

Questo potrebbe essere fatto per len, con una lista? Normalmente, sarebbe simile a questa:

foo = [] 
print(len(foo)) # 0 

foo = [1, 2, 3] 
print(len(foo)) # 3 

Cosa succede se voglio lasciare tipi di ricerca fuori dal conteggio? Come questo:

class Bar(list): 
    pass 

foo = [Bar(), 1, ''] 
print(len(foo)) # 3 

count = 0 
for item in foo: 
    if not isinstance(item, Bar): 
     count += 1 

print(count)  # 2 

C'è un modo per fare questo all'interno di un list sottoclasse?

+3

Hai già buone risposte, ma nel caso in cui si desidera saperne di più sui metodi di magia che posso suggerire [Una guida ai metodi magici di Python] (http: // www. rafekettler.com/magicmethods.html);) – swenzel

+0

Questo ti tornerà utile un bel po '! Grazie per la condivisione. @swenzel –

+0

Hai una sottoclasse lista, non una superclasse lista. – wim

risposta

19

Sì, attuare il __len__ method:

def __len__(self): 
    return 42 

Demo:

>>> class Foo(object): 
...  def __len__(self): 
...   return 42 
... 
>>> len(Foo()) 
42 

Dalla documentazione:

Chiamato per implementare la funzione built-in len(). Dovrebbe restituire la lunghezza dell'oggetto, un numero intero >= 0. Inoltre, un oggetto che non definisce un metodo __bool__() e il cui metodo __len__() restituisce zero è considerato falso in un contesto booleano.

Per il vostro caso specifico:

>>> class Bar(list): 
...  def __len__(self): 
...   return sum(1 for ob in self if not isinstance(ob, Bar)) 
... 
>>> len(Bar([1, 2, 3])) 
3 
>>> len(Bar([1, 2, 3, Bar()])) 
3 
+0

Sarebbe 'sum (non isinstance (ob, Bar) per ob in self)' essere un po 'più pulito? – TigerhawkT3

+0

@ TigerhawkT3: a questo punto devi spiegare che il tipo booleano di Python è una sottoclasse di int e che 'True' ha un valore intero di 1 e' False' un valore di 0. Non vale sempre la pena avere un po 'meno codice quando devi spiegarlo ogni volta. –

+0

Molto vero; Non ci avevo pensato in quel modo. – TigerhawkT3

7

Sì, proprio come avete già scoperto che è possibile ignorare il comportamento di una chiamata repr() funzioni attuando il metodo magico __repr__, è possibile specificare il comportamento da una chiamata di funzione len() implementando (sorpresa sorpresa) poi __len__ magia:

>>> class Thing: 
...  def __len__(self): 
...   return 123 
...  
>>> len(Thing()) 
123 

un pedante potrebbe dire che non stai modificando il comportamento di len(), stai modificando il comportamento della tua classe. len fa esattamente la stessa cosa che fa sempre, che include il controllo di un attributo __len__ sull'argomento.

3

È possibile semplicemente aggiungere un metodo __len__ alla classe.

class Test: 
    def __len__(self): 
     return 2 

a=Test() 
len(a) # --> 2 
5

Ricorda: Python è un linguaggio dinamico e Duck Typed.

Se si comporta come qualcosa che potrebbe avere una lunghezza;

class MyCollection(object): 

    def __len__(self): 
     return 1234 

Esempio:

>>> obj = MyCollection() 
>>> len(obj) 
1234 

se non si comporta come esso ha una lunghezza;KABOOM!

class Foo(object): 

    def __repr___(self): 
     return "<Foo>" 

Esempio:

>>> try: 
...  obj = Foo() 
...  len(obj) 
... except: 
...  raise 
... 
Traceback (most recent call last): 
    File "<stdin>", line 3, in <module> 
TypeError: object of type 'Foo' has no len() 

Da Typing:

Python usa la tipizzazione anatra e ha digitato oggetti ma non tipizzati nomi delle variabili. I vincoli di tipo non vengono controllati in fase di compilazione; piuttosto, le operazioni su un oggetto potrebbero fallire, a significare che l'oggetto dato è non di un tipo adatto. Pur essendo digitato in modo dinamico, Python è fortemente digitato, operazioni proibite che non sono ben definite (per esempio , aggiungendo un numero a una stringa) invece di tentare in silenzio per dare un senso a loro.

Esempio:

>>> x = 1234 
>>> s = "1234" 
>>> x + s 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
TypeError: unsupported operand type(s) for +: 'int' and 'str' 
Problemi correlati