significado - pasar una lista como parametro en python
¿Cómo manejar constructores o métodos con un conjunto diferente(o tipo) de argumentos en Python? (7)
¿Hay alguna manera en Python de tener más de un constructor o más de un método con el mismo nombre , que difieren en el número de argumentos que aceptan o el tipo (s) de uno o más argumentos ?
Si no, ¿cuál sería la mejor manera de manejar tales situaciones?
Por ejemplo, inventé una clase de color. Esta clase solo debería funcionar como un ejemplo básico para discutir esto , hay muchas cosas innecesarias y / o redundantes allí.
Sería bueno, si pudiera llamar al constructor con diferentes objetos (una lista, un otro objeto de color o tres enteros ...) y el constructor los maneja en consecuencia. En este ejemplo básico, funciona en algunos casos con * args y * * kwargs, pero el uso de métodos de clase es la única forma general en que se me ocurrió. ¿Cuál sería una solución de " mejores prácticas " para esto?
Dejando a un lado el constructor, si me gustaría implementar un método _ _ add _ _ también, ¿cómo puedo hacer que este método acepte todo esto: un entero simple (que se agrega a todos los valores), tres enteros (donde el primero es agregado al valor rojo, etc.) u otro objeto de color (donde ambos valores rojos se suman, etc.)?
EDITAR
Agregué un constructor alternativo (inicializador, _ _ init _ _) que básicamente hace todo lo que quería.
Pero me quedo con el primero y los métodos de fábrica. Parece más claro.
También agregué un _ _ agregar _ _, que hace todas las cosas mencionadas anteriormente pero no estoy seguro si es un buen estilo . Intento utilizar el protocolo de iteración y volver al "modo de valor único" en lugar de buscar tipos específicos. Tal vez todavía feo aunque.
He echado un vistazo a _ _ nuevo _ _, gracias por los enlaces.
Mi primer intento rápido falla: filtrar los valores de rgb de * args y * * kwargs (¿es una clase, una lista, etc.) y luego llamar a la superclase _ _ nueva _ _ con las args correctas (solo r, g, b) pasarlo a init. La llamada a ''Super (cls, self) ._ _ new _ _ (....)'' funciona, pero desde que genero y devuelvo el mismo objeto como el que llamo desde (como estaba previsto), todos los argumentos originales pasar a _ _ init _ _ (trabajando como se esperaba), por lo que se libera.
Podría deshacerme del _ _ init _ _ completamente y establecer los valores en _ _ nuevo _ _ pero no sé ... parece que estoy abusando de cosas aquí ;-) Debería echarle un buen vistazo a metaclasses y nuevo primero, supongo.
Fuente:
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
class Color (object):
# It''s strict on what to accept, but I kinda like it that way.
def __init__(self, r=0, g=0, b=0):
self.r = r
self.g = g
self.b = b
# Maybe this would be a better __init__?
# The first may be more clear but this could handle way more cases...
# I like the first more though. What do you think?
#
#def __init__(self, obj):
# self.r, self.g, self.b = list(obj)[:3]
# This methods allows to use lists longer than 3 items (eg. rgba), where
# ''Color(*alist)'' would bail
@classmethod
def from_List(cls, alist):
r, g, b = alist[:3]
return cls(r, g, b)
# So we could use dicts with more keys than rgb keys, where
# ''Color(**adict)'' would bail
@classmethod
def from_Dict(cls, adict):
return cls(adict[''r''], adict[''g''], adict[''b''])
# This should theoreticaly work with every object that''s iterable.
# Maybe that''s more intuitive duck typing than to rely on an object
# to have an as_List() methode or similar.
@classmethod
def from_Object(cls, obj):
return cls.from_List(list(obj))
def __str__(self):
return "<Color(%s, %s, %s)>" % (self.r, self.g, self.b)
def _set_rgb(self, r, g, b):
self.r = r
self.g = g
self.b = b
def _get_rgb(self):
return (self.r, self.g, self.b)
rgb = property(_get_rgb, _set_rgb)
def as_List(self):
return [self.r, self.g, self.b]
def __iter__(self):
return (c for c in (self.r, self.g, self.b))
# We could add a single value (to all colorvalues) or a list of three
# (or more) values (from any object supporting the iterator protocoll)
# one for each colorvalue
def __add__(self, obj):
r, g, b = self.r, self.g, self.b
try:
ra, ga, ba = list(obj)[:3]
except TypeError:
ra = ga = ba = obj
r += ra
g += ga
b += ba
return Color(*Color.check_rgb(r, g, b))
@staticmethod
def check_rgb(*vals):
ret = []
for c in vals:
c = int(c)
c = min(c, 255)
c = max(c, 0)
ret.append(c)
return ret
class ColorAlpha(Color):
def __init__(self, r=0, g=0, b=0, alpha=255):
Color.__init__(self, r, g, b)
self.alpha = alpha
def __str__(self):
return "<Color(%s, %s, %s, %s)>" % (self.r, self.g, self.b, self.alpha)
# ...
if __name__ == ''__main__'':
l = (220, 0, 70)
la = (57, 58, 61, 255)
d = {''r'': 220, ''g'': 0, ''b'':70}
da = {''r'': 57, ''g'': 58, ''b'':61, ''a'':255}
c = Color(); print c # <Color(0, 0, 0)>
ca = ColorAlpha(*la); print ca # <Color(57, 58, 61, 255)>
print ''---''
c = Color(220, 0, 70); print c # <Color(220, 0, 70)>
c = Color(*l); print c # <Color(220, 0, 70)>
#c = Color(*la); print c # -> Fail
c = Color(**d); print c # <Color(220, 0, 70)>
#c = Color(**da); print c # -> Fail
print ''---''
c = Color.from_Object(c); print c # <Color(220, 0, 70)>
c = Color.from_Object(ca); print c # <Color(57, 58, 61, 255)>
c = Color.from_List(l); print c # <Color(220, 0, 70)>
c = Color.from_List(la); print c # <Color(57, 58, 61, 255)>
c = Color.from_Dict(d); print c # <Color(220, 0, 70)>
c = Color.from_Dict(da); print c # <Color(57, 58, 61, 255)>
print ''---''
print ''Check ='', Color.check_rgb(''1'', 0x29a, -23, 40)
# Check = [1, 255, 0, 40]
print ''%s + %s = %s'' % (c, 10, c + 10)
# <Color(57, 58, 61)> + 10 = <Color(67, 68, 71)>
print ''%s + %s = %s'' % (c, ca, c + ca)
# <Color(57, 58, 61)> + <Color(57, 58, 61, 255)> = <Color(114, 116, 122)>
Puede verificar el tipo de argumento pasado a su constructor dentro:
def __init__(self, r = 0, g = 0, b = 0):
# if r is a list
if (type(r) == type([1,2,3])):
r, g, b = r[0], r[1], r[2]
# if r is a color
if (type(r) == type(self)):
r, g, b = r.r, r.g, r.b
self.r = r
self.g = g
self.b = b
Tal vez eso ayude.
En el tema __add__
:
Primero, no puedes obtener "tres enteros", supongo que te refieres a una 3-tupla de enteros.
En ese caso, no isinstance
algunas llamadas a isinstance
:
def __add__(self, other):
if isinstance(other, Color):
...
elif isinstance(other, (int, long)):
...
elif len(other) == 3 and all(isinstance(e, (int, long)) for e in other):
...
else:
raise TypeError("Can only add Color to Color, int or three-tuple")
También es posible que desee agregar implementaciones de __radd__
, para que pueda manejar
1 + Color(1, 2, 3)
pero eso es solo
def __radd__(self, other):
return self.__add__(other)
aunque estrictamente, nunca se llamará cuando el type(other) is Color
.
Además, no olvide __iadd__
para admitir +=
.
En general, use métodos de fábrica, marcados como @classmethod
s. También funcionarán correctamente en subclases. Desde una perspectiva de diseño, son más explícitos, especialmente cuando se les da un buen nombre.
En este caso, mezclar todo junto es probablemente más conveniente, pero también dificulta el contrato para su constructor.
Puede tener los métodos de fábrica, está bien. Pero ¿por qué no simplemente llamarlo como es?
Color(r, g, b)
Color(*[r, g, b])
Color(**{''r'': r, ''g'': g, ''b'': b})
Esta es la forma de pitón. En cuanto al constructor de objetos desde, preferiría algo como:
Color(*Color2.as_list())
Explícito es mejor que implícito - Python Zen
Python no acepta múltiples métodos con el mismo nombre, punto. Un método hace una cosa.
He visto diferentes enfoques recomendados sobre cómo manejar este ... classmethods (como lo describió anteriormente) o funciones de fábrica. Me gustan más los argumentos de palabra clave.
class Color (object):
def __init__(self, **parms):
if parms.get(''list''):
self.r, self.g, self.b = parms[''list'']
elif parms.get(''color''):
color = parms[''color'']
self.r = color.r
self.g = color.g
self.b = color.b
else:
self.r = parms[''red'']
self.g = parms[''green'']
self.b = parms[''blue'']
c1 = Color(red=220, green=0, blue=270)
c2 = Color(list=[220, 0, 70])
c3 = Color(color=c1)
Esto se ajusta a la forma de Python de ser explícito y legible, además de que le permite agregar nuevos argumentos si es necesario.
EDITAR: Además, no tengo que mirar el código de constructor real para entender los argumentos. La explicación es proporcionada por la palabra clave.
Python siempre reemplaza completamente los métodos con el mismo nombre. A diferencia de C # que, si no recuerdo mal, hará que los métodos con las mismas opciones de nombre para la entrada de diferentes argumentos.
Si solo hay una variación de uno en las palabras clave, como 3 o 4 argumentos del mismo tipo, diría que usar un preajuste del último argumento, o todos, sería el camino a seguir.
Sin embargo, si quieres listas, tuplas y otros tipos, probablemente deberías ir a la lista de argumentos arbitrarios y probar el contenido de eso en la función
def function(*args):
if type(args[0]) is int:
dothis()
#and so on
Mi primer consejo es usar métodos de fábrica.
Aunque si realmente quieres un método único, dale algo para enviar para manejar los parámetros.
def __init__(self, method, *args, **kw):
getattr(self, ''_init_''+method)(*args, **kw)
def _init_coponents(self, r, g, b):
...
def _init_fromColor(self, color):
...
Y use como:
c1 = Color(''components'', 0, 0, 0,)
c2 = Color(''fromColor'', c1)
Si bien esto agrega otro parámetro, todavía es mucho mejor que las pruebas de tipo y mantiene las cosas explícitas. Proporciona buenas excepciones listas para usar en llamadas ilegales, y es fácilmente extensible incluso en subclases.