Domanda

Dire che sto dichiarando una classe C e alcune dichiarazioni sono molto simili. Vorrei usare una funzione f per ridurre la ripetizione del codice per queste dichiarazioni. È possibile dichiarare e utilizzare f come al solito:

>>> class C(object):
...     def f(num):
...             return '<' + str(num) + '>'
...     v = f(9)
...     w = f(42)
... 
>>> C.v
'<9>'
>>> C.w
'<42>'
>>> C.f(4)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unbound method f() must be called with C instance as first argument (got int instance instead)

Oops! Ho inavvertitamente esposto f al mondo esterno, ma non accetta un argomento self (e non posso farlo per ovvie ragioni). Una possibilità sarebbe quella di del dopo averla usata:

>>> class C(object):
...     def f(num):
...             return '<' + str(num) + '>'
...     v = f(9)
...     del f
... 
>>> C.v
'<9>'
>>> C.f
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: type object 'C' has no attribute 'f'

Ma cosa succede se voglio usare f più tardi, dopo la dichiarazione? Non farà per cancellare la funzione. Potrei renderlo "privato" (ad esempio, prefisso il nome con __ ) e assegnagli il trattamento @staticmethod , ma invocare oggetti staticmethod attraverso canali anomali diventa molto funky:

>>> class C(object):
...     @staticmethod
...     def __f(num):
...             return '<' + str(num) + '>'
...     v = __f.__get__(1)(9)   # argument to __get__ is ignored...
... 
>>> C.v
'<9>'

Devo usare la follia di cui sopra perché gli oggetti staticmethod , che sono descrittori, non sono essi stessi richiamabili. Devo recuperare la funzione racchiusa dall'oggetto staticmethod prima di poterla chiamare.

Deve esserci un modo migliore per farlo. Come posso dichiarare chiaramente una funzione in una classe, usarla durante la sua dichiarazione e anche usarla successivamente all'interno della classe? Dovrei anche farlo?

È stato utile?

Soluzione

Molto semplicemente, la soluzione è che f non ha bisogno di essere un membro della classe. Suppongo che il tuo processo mentale abbia attraversato un filtro in linguaggio Java che ha causato il blocco mentale. Va un po 'qualcosa del genere:

def f(n):
    return '<' + str(num) + '>'

class C(object):

    v = f(9)
    w = f(42)

Quindi quando vuoi usare di nuovo f, basta usarlo

>>> f(4)
'<4>'

Penso che la morale del racconto sia " In Python, non devi forzare tutto in una classe " ;.

Altri suggerimenti

Estensione Ali A la risposta, se vuoi davvero evitare f nello spazio dei nomi del modulo (e usare un nome non esportato come _f, o impostare __all__ non è sufficiente), allora potresti raggiungere questo obiettivo creando la classe all'interno di una chiusura.

def create_C():
    def f(num):
        return '<' + str(num) + '>'

    class C(object):
        v = f(9)
        def method_using_f(self, x):  return f(x*2)
    return C

C=create_C()
del create_C

In questo modo C ha accesso a f all'interno della sua definizione e dei suoi metodi, ma nient'altro fa (salvo l'introspezione abbastanza coinvolta dei suoi metodi ( C.method_using_f.im_func.func_closure ))

Questo è probabilmente eccessivo per la maggior parte degli scopi - documentare che f è interno usando " _ " la convenzione di denominazione del prefisso dovrebbe generalmente essere sufficiente.

[Modifica] Un'altra opzione è quella di mantenere un riferimento all'oggetto funzione preconfezionato nei metodi in cui si desidera utilizzarlo. Ad esempio, impostandolo come argomento predefinito:

class C(object):
    def f(num):
        return '<' + str(num) + '>'

    v = f(9)
    def method_using_f(self, x, f=f):  return f(x*2)

    del f

(Anche se penso che l'approccio di chiusura sia probabilmente migliore)

Questa è una possibilità:

class _C:
    # Do most of the function definitions in here
    @classmethod
    def f(cls):
        return 'boo'

class C(_C):
    # Do the subsequent decoration in here
    v = _C.f()

Credo che tu stia provando a farlo:

class C():
...     class F():
...         def __call__(self,num):
...             return "<"+str(num)+">"
...     f=F()
...     v=f(9)
>>> C.v
'<9>'
>>> C.f(25)
'<25>'
>>> 

Forse esiste una soluzione migliore o più pitonica ...

  

" dichiarare una funzione in una classe, usarla durante la sua dichiarazione e usarla anche successivamente all'interno della classe "

     

Siamo spiacenti. Non si può fare.

" Impossibile eseguire " non sembra andare d'accordo con Python

Un'opzione: scrivere un staticmethod migliore:

class staticfunc(object):
    def __init__(self, func):
        self.func = func
    def __call__(self, *args, **kw):
        return self.func(*args, **kw)
    def __repr__(self):
        return 'staticfunc(%r)' % self.func

Cominciamo dall'inizio.

" dichiarare una funzione in una classe, usarla durante la sua dichiarazione e usarla anche successivamente all'interno della classe "

Siamo spiacenti. Non si può fare " In una classe " contraddizioni "utilizzate durante la dichiarazione".

  • In una classe significa creato come parte della dichiarazione.
  • Utilizzato durante la dichiarazione significa che esiste al di fuori della classe. Spesso come meta-classe. Tuttavia, ci sono altri modi.

Non è chiaro cosa dovrebbero essere C.w e C.v. Sono solo stringhe? In tal caso, una funzione esterna f è la soluzione migliore. & Quot; non ingombra lo spazio dei nomi " è un po 'specioso. Dopotutto, vuoi usarlo di nuovo.

È nello stesso modulo di C. Ecco perché Python ha dei moduli. Associa la funzione e la classe insieme.

import myCmod

myCmod.C.w
myCmod.C.v
myCmod.f(42)

Se w e v non sono stringhe semplici, esiste davvero un'ottima soluzione che offre molta flessibilità.

Generalmente, per variabili a livello di classe ("quotate statiche") come questa, possiamo usare altre classi. Non è possibile ottenere completamente l'API desiderata, ma questa è vicina.

>>> class F(object):
    def __init__( self, num ):
        self.value= num
        self.format= "<%d>" % ( num, )

>>> class C(object):
    w= F(42)
    v= F(9)

>>> C.w
<__main__.F object at 0x00C58C30>
>>> C.w.format
'<42>'
>>> C.v.format
'<9>'

Il vantaggio di questo è che F è una cosa vera e di prima classe che può essere estesa. Non un " nascosto " cosa che stiamo cercando di evitare di esporre. È un dato di fatto, quindi potremmo anche seguire il principio Open / Closed e renderlo aperto all'estensione.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top