2012-10-11 21 views
5

Después de leer un poco, me encontré luchando con dos enfoques diferentes para pasar una lista de argumentos a una función. Leí algunas indicaciones. Eso es lo que me di cuenta hasta ahora:Mejor enfoque para pasar y manejar argumentos para funcionar

código real:

archivo caller.py:

import worker 
worker.version_check(iserver,login,password,proxyUser,proxyPass, 
    proxyServer,packageInfo) 

worker.version_get(iserver,login,password,proxyUser,proxyPass, 
    proxyServer,packageInfo) 

worker.version_send(iserver,login,password,proxyUser,proxyPass, 
    proxyServer,packageInfo) 

del archivo: worker.py:

def version_check(iserver,login,password,proxyUser,proxyPass,proxyServer,service): 
    #code and more code 

def version_get(iserver,login,password,proxyUser,proxyPass,proxyServer,service): 
    #code and more code 

def version_send(iserver,login,password,proxyUser,proxyPass,proxyServer,service): 
    #code and more code 

Y ahora tengo:

file caller.py:

import worker 
args = (env, family, host, password, prefix, proxyServer, 
     proxyUser, proxyPass, option, jokerVar 
     ) 
worker.version_check(*args) 
worker.version_get(*args) 
worker.version_send(*args) 

del archivo: worker.py:

def version_check(*args): 
    env = args[0] 
    family = args[1] 
    host = args[2] 
    password = args[3] 
    prefix = args[4] 
    proxyServer = args[5] 
    proxyUser = args[6] 
    proxyPass = args[7] 
    option = args[8] 
    jokerVar = args[9] 

    #code and more code 

def version_get((*args): 
    env = args[0] 
    family = args[1] 
    host = args[2] 
    password = args[3] 
    prefix = args[4] 
    proxyServer = args[5] 
    proxyUser = args[6] 
    proxyPass = args[7] 
    option = args[8] 
    jokerVar = args[9] 

    #code and more code 

def version_send(*args): 
    env = args[0] 
    family = args[1] 
    host = args[2] 
    password = args[3] 
    prefix = args[4] 
    proxyServer = args[5] 
    proxyUser = args[6] 
    proxyPass = args[7] 
    option = args[8] 
    jokerVar = args[9] 

    #code and more code 

Utilizando el enfoque de edad (código real) Creo que es más "amigable" para llamar a una función en una sola línea (como se puede ver en worker.py) Pero, utilizando el nuevo enfoque, creo que el código se vuelve más extenso porque para cada función tengo que definir todas las mismas variables. ¿Pero esta es la mejor práctica? Todavía estoy aprendiendo Python en una curva lenta, así que, lo siento por cualquier error en el código.

Y una cosa importante, la mayoría de las variables se recuperan de una base de datos, por lo que no son estáticas.

+5

En general, cuando se termina con las funciones que con muchos argumentos, es necesario pensar en su diseño un poco más. ¿Sería mejor pasar un objeto que muchos argumentos? –

+0

@MartijnPieters ¿Es posible enviarme un ejemplo? Mientras tanto, trataré de encontrarlo también en el s.o. y documentación de Python. –

+1

¿Qué representa el conjunto de argumentos? ¿Puede eso ser nombrado? Si es así, conviértalo en una clase personalizada o personalizada para que contenga esa información, luego tendrá que pasar un objeto en lugar de x argumentos separados. –

Respuesta

6

realmente no recomiendan definir funciones como def version_check(*args): a menos que específicamente necesidad a. Rápido, sin leer la fuente: ¿en qué orden están los argumentos? ¿Cómo se especifica un valor predeterminado para proxyServer? Recuerde, "explícito es mejor que implícito".

La única vez que rutinariamente desviarse de dicha regla es cuando estoy envolviendo otra función como:

def foo(bar): 
    print 'Bar:', bar 

def baz(qux, *args): 
    print 'Qux:', qux 
    foo(*args) 

Nunca lo haría para un ejemplo tan simple, pero supongo foo es una función de una Un paquete de terceros fuera de mi control con un montón de valores predeterminados, argumentos de palabras clave, etc. En ese caso, preferiría puntualizar el argumento de análisis en Python que intentarlo yo mismo.

En lo personal, me gustaría escribir que a medida que una clase como:

class Worker(object): 
    def __init__(iserver,login,password,proxyUser,proxyPass,proxyServer,service): 
     self.iserver = iserver 
     self.login = login 
     self.password = password 
     self.proxyUser = proxyUser 
     self.proxyPass = proxyPass 
     self.proxyServer = proxyServer 
     self.service = service 

    def version_check(self): ... 

    def version_get(self): ... 

    def version_send(self): ... 

Y luego en el cliente, escribe:

from worker import Worker 

w = Worker(iserver,login,password,proxyUser,proxyPass,proxyServer,service) 
w.version_check() 
w.version_get() 
w.version_send() 

Si realmente necesita para escribir funciones con gran cantidad de argumentos en vez de encapsular ese estado en una clase, que es una manera más típicamente pitoniana de hacerlo, entonces considere el tipo de datos namedtuple de las versiones recientes de Python.Le permite especificar una tupla en la que los elementos son direccionables por palabra clave y puede generar un código muy limpio y elegante.

+0

+1! Esta es una respuesta genial. Usted dio la misma solución que yo habría dado (encapsulando las funciones y los datos en una clase) y dio una explicación muy clara de por qué la versión original no es tan buena. – Blckknght

+0

+1 también podría [crear más de un objeto] (http://stackoverflow.com/questions/12844963/best-approach-to-pass-and-handle-arguments-to-function#comment17382149_12844963) – jfs

+0

@Blckknght Gracias ! Lo intento. :-) –

0

Puede crear una instancia de un objeto o definir una clase. p.ej.

archivo caller.py:

import worker 

info=object() 
info.env=0 
info.family='something' 
info.host='something' 
info.password='***' 
info.prefix='' 
info.proxyServer='' 
info.proxyUser='' 
info.proxyPass='' 
info.option='' 
info.jokerVar='' 

worker.version_check(info) 
worker.version_get(info) 
worker.version_send(info) 

archivo worker.py:

def version_check(info): 
    #you may access values from info 
    #code and more code 

def version_get(info): 
    #code and more code 

def version_send(info): 
    #code and more code 
+0

no puede establecer atributos arbitrarios en 'object' directamente. 'class Info: pass' se puede usar en su lugar. De todos modos, esta aplicación no es mejor que simplemente usar argumentos de palabra clave, ver [elemento 1 en @Francis 'respuesta] (http://stackoverflow.com/a/12847386/4279) – jfs

2

Hay muchos enfoques, dependiendo de lo que representen esos argumentos.

  1. Si no son más que un cajón de sastre de argumentos (especialmente si algunos son opcionales), utilice argumentos clave:

    myargs = {'iserver':'server','login':'username','password':'Pa2230rd'} 
    version_get(**myargs) 
    
  2. Si representan algo con su propio estado, a continuación, utilizar clases:

    1. Si los argumentos representan un solo estado que su func ciones están modificando, a continuación, aceptar los argumentos del constructor de objetos y hacer que sus version_* métodos funciones de esa clase:

      class Version(object): 
          def __init__(self,iserver,login,password, 
             proxyUser,proxyPass,proxyServer,service): 
           self.iserver = iserver 
           self.login = login 
           #etc 
          def check(self): 
           self.iserver 
          def get(self): 
           pass 
          #etc 
      myversion = Version('iserver','login',...) 
      myversion.check() 
      
    2. Si tiene algún tipo de recurso esos argumentos representar que sus funciones son meramente utilizando, en ese caso utilizar una clase separada, y la suministra como un parámetro del objeto a sus funciones:

      class Connection(Object): 
          def __init__(self, iserver, ...): 
           self.iserver # etc 
      
      myconn = Connection('iserver',...) 
      
      version_check(myconn) 
      
    3. lo más probable, se trata de dos recursos diferentes y deben ser de dos clases. En este caso se puede combinar estos enfoques:

      #Connection() class as above 
      class Version(object): 
          def __init__(self, connection): 
           self.connection = connection 
          def check(self): 
           self.connection.iserver # .... 
      
      myconn = Connection('iserver', ...) 
      conn_versioner = Version(myconn) 
      conn_versioner.check() 
      
    4. Posiblemente, sus argumentos representan más de un objeto (por ejemplo, una conexión y un objeto proxy transparente) En ese caso, tratan de crear un objeto con el público más pequeño los métodos de interfaz como version_* necesitarían y encapsularían el estado representado por los otros argumentos utilizando la composición del objeto.

      Por ejemplo, si usted tiene conexiones proxy, puede crear una clase Connection() que acaba sabe de servidor, nombre de usuario y contraseña, y una clase ConnectionProxy() que tiene todos los métodos de una Connection, pero hacia delante a otro objeto Connection. Esto le permite separar los argumentos proxy*, y significa que sus funciones version_* pueden ignorar si están usando un proxy o no.

  3. Si sus argumentos son solo estado y no tienen ningún métodos apropiados para ellos, considerar el uso de un namedtuple(). Esto actuará como una tupla más inteligente (que incluye el desempaquetado de tuplas, el corte en rebanadas, etc.) y tendrá un impacto mínimo en el código existente, a la vez que será más fácil de usar.

    Connection = namedtuple('Connection', 'iserver login password etc') 
    myconn = Connection('iserver', 'loginname', 'passw3rd') 
    version_check(*myconn) 
    
Cuestiones relacionadas