Pregunta

Digamos que estoy declarando una clase C y algunas de las declaraciones son muy similares. Me gustaría usar una función f para reducir la repetición de código para estas declaraciones. Es posible declarar y usar f como de costumbre:

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

¡Vaya! Inadvertidamente expuse f al mundo exterior, pero no toma un argumento self (y no puede por razones obvias). Una posibilidad sería del la función después de usarla:

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

¿Pero qué pasa si quiero usar f nuevamente más tarde, después de la declaración? No servirá para eliminar la función. Podría hacerlo "privado" (es decir, prefije su nombre con __ ) y dele el tratamiento @staticmethod , pero invocar objetos staticmethod a través de canales anormales se vuelve muy raro:

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

Tengo que usar la locura anterior porque los objetos staticmethod , que son descriptores, no son invocables. Necesito recuperar la función envuelta por el objeto staticmethod antes de poder llamarlo.

Tiene que haber una mejor manera de hacer esto. ¿Cómo puedo declarar limpiamente una función en una clase, usarla durante su declaración y también usarla más tarde desde dentro de la clase? ¿Debería incluso hacer esto?

¿Fue útil?

Solución

Simplemente, la solución es que f no necesita ser miembro de la clase. Supongo que su proceso de pensamiento ha pasado por un filtro de lenguaje Java que está causando el bloqueo mental. Va un poco más o menos así:

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

class C(object):

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

Luego, cuando quieras volver a usar f, simplemente úsalo

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

Creo que la moraleja del cuento es "En Python, no tienes para forzar todo en una clase".

Otros consejos

Extendiendo Ali A la respuesta si realmente desea evitar f en el espacio de nombres del módulo (y usar un nombre no exportado como _f, o establecer __todos__ no es suficiente), entonces podría lograr esto creando la clase dentro de un cierre.

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

De esta forma, C tiene acceso a f dentro de su definición y métodos, pero nada más (salvo la introspección bastante complicada) de sus métodos ( C.method_using_f.im_func.func_closure ))

Sin embargo, esto es probablemente exagerado para la mayoría de los propósitos: documentar que f es interno mediante el uso de " _ " convenio de denominación de prefijo debe generalmente sea suficiente.

[Editar] Otra opción es mantener una referencia al objeto de función preenvasado en los métodos en los que desea utilizarlo. Por ejemplo, configurándolo como argumento predeterminado:

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

(Aunque creo que el enfoque de cierre es probablemente mejor)

Esta es una posibilidad:

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

Creo que estás tratando de hacer esto:

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

Quizás haya una solución pitónica mejor o más ...

  

" declarar una función en una clase, usarla durante su declaración, y también usarla más tarde desde dentro de la clase "

     

Lo siento. No se puede hacer.

" No se puede hacer " no parece llevarse bien con Python

Una opción: escriba un mejor staticmethod :

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

Comencemos desde el principio.

" declara una función en una clase, úsala durante su declaración y úsala más tarde desde dentro de la clase "

Lo siento. No se puede hacer " En una clase " contradice " usado durante la declaración " ;.

  • En una clase significa creado como parte de la declaración.
  • Utilizado durante la declaración significa que existe fuera de la clase. A menudo como una metaclase. Sin embargo, hay otras formas.

No está claro qué se supone que son C.w y C.v. ¿Son solo cuerdas? Si es así, una función externa f es la mejor solución. El " no satura el espacio de nombres " Es un poco engañoso. Después de todo, desea usarlo nuevamente.

Está en el mismo módulo que C. Es por eso que Python tiene módulos. Vincula la función y la clase juntas.

import myCmod

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

Si w y v no son cadenas simples, hay una solución realmente buena que brinda mucha flexibilidad.

Generalmente, para variables de nivel de clase ("estáticas") como esta, podemos usar otras clases. No es posible lograr completamente la API deseada, pero esto está cerca.

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

La ventaja de esto es que F es una cosa adecuada de primera clase que se puede extender. No es un "oculto" Lo que estamos tratando de evitar exponer. Es un hecho de la vida, por lo que podríamos seguir el principio de Abrir / Cerrado y hacerlo abierto a la extensión.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top