procedimientos - lista de funciones de python
Asignar argumentos de función a `self` (10)
Renuncia
No use esto: simplemente estaba tratando de crear la respuesta más cercana a las intenciones iniciales de los OP. Como se señaló en los comentarios, esto se basa en un comportamiento totalmente indefinido , y prohibió explícitamente las modificaciones de la tabla de símbolos.
Sin embargo, funciona, y ha sido probado en circunstancias extremadamente básicas .
Solución
class SomeClass():
def __init__(self, a, b, c):
vars(self).update(dict((k,v) for k,v in vars().iteritems() if (k != ''self'')))
sc = SomeClass(1, 2, 3)
# sc.a == 1
# sc.b == 2
# sc.c == 3
Usando la función incorporada vars()
, este fragmento de código itera a través de todas las variables disponibles en el método __init__
(que en este punto debería ser solo self
, a
, b
, c
) y las variables self
del conjunto son iguales a lo mismo, obviamente ignorando el argumento-referencia a self
(porque el self.self
parecía una mala decisión).
Me he dado cuenta de que un patrón común que utilizo es asignar los SomeClass.__init__()
a los atributos SomeClass.__init__()
del mismo nombre. Ejemplo:
class SomeClass():
def __init__(self, a, b, c):
self.a = a
self.b = b
self.c = c
De hecho, debe ser una tarea común para los demás, ya que PyDev tiene un atajo para esto: si coloca el cursor en la lista de parámetros y hace clic en Ctrl+1
se le da la opción de Assign parameters to attributes
que crearán ese texto. código para usted.
¿Hay una manera diferente, corta y elegante de realizar esta tarea?
Lo resolví por mí mismo usando locals()
y __dict__
:
>>> class Test:
... def __init__(self, a, b, c):
... l = locals()
... for key in l:
... self.__dict__[key] = l[key]
...
>>> t = Test(1, 2, 3)
>>> t.a
1
>>>
Mod para la respuesta de @ pcperini:
>>> class SomeClass():
def __init__(self, a, b=1, c=2):
for for name,value in vars().iteritems():
if name != ''self'':
setattr(self,name,value)
>>> s = SomeClass(7,8)
>>> print s.a,s.b,s.c
7 8 2
Para ese caso de uso simple, debo decir que me gusta poner las cosas explícitamente (usando Ctrl + 1 de PyDev), pero a veces también termino usando una implementación en grupo, pero con una clase donde los atributos aceptados se crean a partir de atributos declarados previamente en la clase, para que sepa lo que se espera (y me gusta más que un grupo con nombre, ya que me resulta más legible, y no confundirá el análisis de código estático o la terminación de código).
He puesto una receta para ello en: http://code.activestate.com/recipes/577999-bunch-class-created-from-attributes-in-class/
La idea básica es que declara su clase como una subclase de Bunch y creará esos atributos en la instancia (ya sea por defecto o por los valores pasados en el constructor):
class Point(Bunch):
x = 0
y = 0
p0 = Point()
assert p0.x == 0
assert p0.y == 0
p1 = Point(x=10, y=20)
assert p1.x == 10
assert p1.y == 20
Además, Alex Martelli también proporcionó una gran cantidad de implementaciones: http://code.activestate.com/recipes/52308-the-simple-but-handy-collector-of-a-bunch-of-named/ con la idea de actualizar el de los argumentos, pero eso confundirá el análisis de código estático (y IMO puede hacer que las cosas sean más difíciles de seguir), por lo tanto, solo uso ese enfoque para una instancia que se crea localmente y se desecha dentro del mismo ámbito sin pasarlo a ninguna parte más).
Podrías hacer esto, que tiene la virtud de la simplicidad:
>>> class C(object):
def __init__(self, **kwargs):
self.__dict__ = dict(kwargs)
Esto lo deja a cualquier código que cree una instancia de C
para decidir cuáles serán los atributos de la instancia después de la construcción, por ejemplo:
>>> c = C(a=''a'', b=''b'', c=''c'')
>>> c.a, c.b, c.c
(''a'', ''b'', ''c'')
Si desea que todos los objetos C
tengan atributos a, b
y c
, este enfoque no será útil.
(Por cierto, este patrón proviene de Guido, su propio yo malo, como una solución general al problema de definir enumeraciones en Python. Cree una clase como la mencionada anteriormente Enum
, y luego puede escribir código como Colors = Enum(Red=0, Green=1, Blue=2)
, y en adelante use Colors.Red
, Colors.Green
, and Colors.Blue
.)
Es un ejercicio que vale la pena averiguar qué tipo de problemas podría tener si se establece self.__dict__
en kwargs
lugar de dict(kwargs)
.
Puedes hacerlo a través de setattr (), como:
[setattr(self, key, value) for key, value in kwargs.items()]
No es muy bonito, pero puede ahorrar algo de espacio :)
Por lo tanto, obtendrá:
kwargs = { ''d'':1, ''e'': 2, ''z'': 3, }
class P():
def __init__(self, **kwargs):
[setattr(self, key, value) for key, value in kwargs.items()]
x = P(**kwargs)
dir(x)
[''__doc__'', ''__init__'', ''__module__'', ''d'', ''e'', ''z'']
Simpatizo con su sentido de que el código repetitivo es algo malo. Pero en este caso, no estoy seguro de que pueda haber una mejor alternativa. Consideremos las posibilidades.
Si está hablando de unas pocas variables, entonces es fácil leer una serie de líneas self.x = x
. De hecho, creo que su explicación hace que este enfoque sea preferible desde el punto de vista de la legibilidad. Y si bien podría ser un poco doloroso escribir, eso solo no es suficiente para justificar una nueva construcción de lenguaje que podría ocultar lo que realmente está sucediendo. Ciertamente, el uso de vars(self).update()
shenanigans sería más confuso de lo que vale en este caso.
Por otro lado, si está pasando nueve, diez o más parámetros a __init__
, probablemente necesite refactorizar de todos modos. Entonces, esta preocupación solo se aplica a los casos que involucran pasar, por ejemplo, 5-8 parámetros. Ahora puedo ver cómo ocho líneas de self.x = x
serían molestas tanto para escribir como para leer; pero no estoy seguro de que el caso de los parámetros de 5 a 8 sea lo suficientemente común o problemático para justificar el uso de un método diferente. Así que creo que, si bien la preocupación que está planteando es buena en principio, en la práctica, existen otros problemas limitantes que la hacen irrelevante.
Para concretar más este punto, consideremos una función que toma un objeto, un dict y una lista de nombres, y asigna valores del dict a los nombres de la lista. Esto asegura que aún estés siendo explícito sobre qué variables se asignan a ti mismo. (Nunca sugeriría una solución a este problema que no requiera una enumeración explícita de las variables que se asignarán; eso sería un imán de error de tierras raras):
>>> def assign_attributes(obj, localdict, names):
... for name in names:
... setattr(obj, name, localdict[name])
...
>>> class SomeClass():
... def __init__(self, a, b, c):
... assign_attributes(self, vars(), [''a'', ''b'', ''c''])
Ahora, aunque no es terriblemente poco atractivo, esto es aún más difícil de entender que una serie directa de líneas self.x = x
. Y también es más largo y más problemático escribir que una, dos y tal vez incluso tres o cuatro líneas, según las circunstancias. Así que solo obtienes cierta recompensa a partir del caso de cinco parámetros. Pero ese es también el momento exacto en el que comienza a acercarse al límite de la capacidad de memoria humana a corto plazo (= 7 +/- 2 "trozos"). Entonces, en este caso, su código ya es un poco difícil de leer, y esto solo lo haría más desafiante.
Su caso específico también podría ser manejado con una pareja nombrada :
>>> from collections import namedtuple
>>> SomeClass = namedtuple("SomeClass", "a b c")
>>> sc = SomeClass(1, "x", 200)
>>> print sc
SomeClass(a=1, b=''x'', c=200)
>>> print sc.a, sc.b, sc.c
1 x 200
Uno de los problemas con la respuesta de @ user3638162 es que locals()
contiene la variable ''self''. Por lo tanto, terminas con un ser extra. Si a uno no le importa el ser extra, esa solución puede ser simplemente
class X:
def __init__(self, a, b, c):
self.__dict__.update(locals())
x = X(1, 2, 3)
print(x.a, x.__dict__)
El self
puede eliminarse después de la construcción por del self.__dict__[''self'']
Alternativamente, uno puede eliminar el yo durante la construcción utilizando las explicaciones del diccionario introducidas en Python3
class X:
def __init__(self, a, b, c):
self.__dict__.update(l for l in locals().items() if l[0] != ''self'')
x = X(1, 2, 3)
print(x.a, x.__dict__)
Decorador de magia !!
>>> class SomeClass():
@ArgsToSelf
def __init__(a, b=1, c=2, d=4, e=5):
pass
>>> s=SomeClass(6,b=7,d=8)
>>> print s.a,s.b,s.c,s.d,s.e
6 7 2 8 5
mientras se define:
>>> import inspect
>>> def ArgsToSelf(f):
def act(self, *args, **kwargs):
arg_names,_,_,defaults = inspect.getargspec(f)
defaults=list(defaults)
for arg in args:
setattr(self, arg_names.pop(0),arg)
for arg_name,arg in kwargs.iteritems():
setattr(self, arg_name,arg)
defaults.pop(arg_names.index(arg_name))
arg_names.remove(arg_name)
for arg_name,arg in zip(arg_names,defaults):
setattr(self, arg_name,arg)
return f(*args, **kwargs)
return act
Por supuesto, puede definir este decorador una vez y utilizarlo en todo su proyecto.
Además, este decorador funciona en cualquier función de objeto, no solo __init__
.