Jindle yugal
Quiero pasar un argumento predeterminado a un método de instancia usando el valor de un atributo de la instancia:
class C:
def __init__(self, format):
self.format = format
def process(self, formatting=self.format):
print(formatting)
Al intentar eso, me sale el siguiente mensaje de error:
NameError: name 'self' is not defined
Quiero que el método se comporte así:
C("abc").process() # prints "abc"
C("abc").process("xyz") # prints "xyz"
¿Cuál es el problema aquí, por qué esto no funciona? ¿Y cómo podría hacer que esto funcione?
Adán Wagner
Realmente no puede definir esto como el valor predeterminado, ya que el valor predeterminado se evalúa cuando se define el método, que es antes de que existan instancias. El patrón habitual es hacer algo como esto en su lugar:
class C:
def __init__(self, format):
self.format = format
def process(self, formatting=None):
if formatting is None:
formatting = self.format
print(formatting)
self.format
sólo se utilizará si formatting
es None
.
Para demostrar el punto de cómo funcionan los valores predeterminados, vea este ejemplo:
def mk_default():
print("mk_default has been called!")
def myfun(foo=mk_default()):
print("myfun has been called.")
print("about to test functions")
myfun("testing")
myfun("testing again")
Y la salida aquí:
mk_default has been called!
about to test functions
myfun has been called.
myfun has been called.
Date cuenta cómo mk_default
fue llamado solo una vez, ¡y eso sucedió antes de que se llamara a la función!
-
Yo creo
mk_default
fue llamado antes de que las funciones fueran llamadas desdefoo=mk_default()
lo llamó, debido al paréntesis. ¿No debería serfoo=mk_default
? Entonces tu ejemplo podría cambiar amyfun("testing")
seguido pormyfun()
.– gary
4 de enero de 2013 a las 2:28
-
Tenga en cuenta que
formatting = formatting or self.format
estableceráformatting
aself.format
siformatting
es un valor falso, como 0. Esto me molesta. Una forma más segura es escribirformatting = formatting if formatting is not None else self.format
o equivalente.– Godsmith
2 oct 2017 a las 19:11
-
@Godsmith, ¡buen punto! He actualizado mi respuesta para dar cuenta de esto … ¡gracias!
– Adán Wagner
2 oct 2017 a las 20:09
Carlos Knechtel
En Python, el nombre self
es no especial. Es solo una convención para el nombre del parámetro, por lo que hay un self
parámetro en __init__
. (De hecho, __init__
tampoco es muy especial, y en particular lo hace no en realidad crear el objeto… esa es una historia más larga)
C("abc").process()
crea un C
ejemplo, busca el process
método en el C
clase, y llama a ese método con el C
instancia como el primer parámetro. Entonces terminará en el self
parámetro si lo proporcionó.
Sin embargo, incluso si tuviera ese parámetro, no se le permitiría escribir algo como def process(self, formatting = self.formatting)
porque self
aún no está dentro del alcance en el punto en el que establece el valor predeterminado. En Python, el valor predeterminado de un parámetro se calcula cuando se compila la función y se “pega” a la función. (Esta es la misma razón por la que, si usa un valor predeterminado como []
esa lista recordará los cambios entre las llamadas a la función).
¿Cómo podría hacer que esto funcione?
La forma tradicional es utilizar None
como predeterminado, verifique ese valor y reemplácelo dentro de la función. Puede encontrar que es un poco más seguro hacer un valor especial para el propósito (un object
instancia es todo lo que necesita, siempre que lo oculte para que el código de llamada no use la misma instancia) en lugar de None
. De cualquier manera, debe verificar este valor con is
no ==
.
-
Su solución alternativa no cumple con el resultado deseado al usar Ninguno.
– Yugal Jindle
15 de noviembre de 2011 a las 5:56
-
Si
None
es un valor válido paraformatting
entonces tendrás que elegir otra cosa, como expliqué.– Karl Knechtel
15 de noviembre de 2011 a las 6:01
un invitado
Ya que quieres usar self.format
como argumento predeterminado, esto implica que el método debe ser específico de la instancia (es decir, no hay forma de definir esto a nivel de clase). En su lugar, puede definir el método específico durante la clase’ __init__
Por ejemplo. Aquí es donde tiene acceso a los atributos específicos de la instancia.
Un enfoque es utilizar functools.partial
para obtener una versión actualizada (específica) del método:
from functools import partial
class C:
def __init__(self, format):
self.format = format
self.process = partial(self.process, formatting=self.format)
def process(self, formatting):
print(formatting)
c = C('default')
c.process()
# c.process('custom') # Doesn't work!
c.process(formatting='custom')
Tenga en cuenta que con este enfoque solo puede pasar el argumento correspondiente por palabra clave, ya que si lo proporcionó por posición, esto crearía un conflicto en partial
.
Otro enfoque es definir y establecer el método en __init__
:
from types import MethodType
class C:
def __init__(self, format):
self.format = format
def process(self, formatting=self.format):
print(formatting)
self.process = MethodType(process, self)
c = C('test')
c.process()
c.process('custom')
c.process(formatting='custom')
Esto también permite pasar el argumento por posición, sin embargo, el orden de resolución del método se vuelve menos evidente (lo que puede afectar la inspección de IDE, por ejemplo, pero supongo que hay soluciones específicas de IDE para eso).
Otro enfoque sería crear un tipo personalizado para este tipo de “valores predeterminados de atributos de instancia” junto con un decorador especial que realice el correspondiente getattr
relleno de argumentos:
import inspect
class Attribute:
def __init__(self, name):
self.name = name
def decorator(method):
signature = inspect.signature(method)
def wrapper(self, *args, **kwargs):
bound = signature.bind(*((self,) + args), **kwargs)
bound.apply_defaults()
bound.arguments.update({k: getattr(self, v.name) for k, v in bound.arguments.items()
if isinstance(v, Attribute)})
return method(*bound.args, **bound.kwargs)
return wrapper
class C:
def __init__(self, format):
self.format = format
@decorator
def process(self, formatting=Attribute('format')):
print(formatting)
c = C('test')
c.process()
c.process('custom')
c.process(formatting='custom')
No puede acceder a sí mismo en la definición del método. Mi solución es esta:
class Test:
def __init__(self):
self.default_v = 20
def test(self, v=None):
v = v or self.default_v
print(v)
Test().test()
> 20
Test().test(10)
> 10
Yajushi
“self” debe pasarse como primer argumento a cualquier función de clase si desea que se comporten como métodos no estáticos.
se refiere al objeto mismo. No podría pasar “self” como argumento predeterminado, ya que su posición es fija como primer argumento.
En su caso, en lugar de “formatting=self.format”, use “formatting=None” y luego asigne el valor del código como se muestra a continuación:
[EDIT]
class c:
def __init__(self, cformat):
self.cformat = cformat
def process(self, formatting=None):
print "Formating---",formatting
if formatting == None:
formatting = self.cformat
print formatting
return formatting
else:
print formatting
return formatting
c("abc").process() # prints "abc"
c("abc").process("xyz") # prints "xyz"
Nota: no use “formato” como nombre de variable, porque es una función integrada en python
-
Bueno, corregí el
self
problema. Pero su respuesta no cumple con el resultado deseado.– Yugal Jindle
15 de noviembre de 2011 a las 5:55
noPounch
En lugar de crear una lista de si-entonces que abarque sus argumentos predeterminados, se puede usar un diccionario ‘predeterminado’ y crear nuevas instancias de una clase usando eval():
class foo():
def __init__(self,arg):
self.arg = arg
class bar():
def __init__(self,*args,**kwargs):
#default values are given in a dictionary
defaults = {'foo1':'foo()','foo2':'foo()'}
for key in defaults.keys():
#if key is passed through kwargs, use that value of that key
if key in kwargs: setattr(self,key,kwargs[key])
#if no key is not passed through kwargs
#create a new instance of the default value
else: setattr(self,key, eval(defaults[key]))
Lanzo esto al comienzo de cada clase que crea una instancia de otra clase como argumento predeterminado. Evita que Python evalúe el valor predeterminado en la compilación … Me encantaría un enfoque pitónico más limpio, pero he aquí.
-
Bueno, corregí el
self
problema. Pero su respuesta no cumple con el resultado deseado.– Yugal Jindle
15 de noviembre de 2011 a las 5:55
no use format como nombre de variable, ya que es una función integrada en python.
– Yajushi
15 de noviembre de 2011 a las 5:43
Editando el error de
self
enprocess method
– Yugal Jindle
15 de noviembre de 2011 a las 5:54
Hay un proyecto de PEP en la tubería, lo que haría esto posible usando
=>
en vez de=
. Actualmente está programado para aparecer en Python 3.12.– Karl Knechtel
5 de septiembre de 2022 a las 9:45