Лучший подход для передачи и обработки аргументов в функции
После некоторого прочтения я столкнулся с двумя разными способами передачи списка аргументов функции. Я прочитал некоторые указания. Вот что я понял до сих пор:
Актуальный код:
файл 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)
Файл: 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
И теперь у меня есть:
файл 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)
Файл: 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
Используя старый подход (реальный код), я считаю, что более "дружественно" вызывать функцию только в одну строку (как вы можете видеть на worker.py). Но, используя новый подход, я думаю, что код становится более обширным, потому что для каждой функции мне приходится определять все одинаковые переменные. Но это лучшая практика? Я все еще изучаю Python на медленной кривой, так что, извините за любые ошибки в коде.
И еще одна важная вещь: большинство переменных извлекаются из базы данных, поэтому они не являются произвольными.
3 ответа
Я действительно не рекомендую определять такие функции, как def version_check(*args):
если вам конкретно не нужно. Быстро, без чтения источника: в каком порядке аргументы? Как указать значение по умолчанию для proxyServer? Помните, "явное лучше, чем неявное".
Единственный раз, когда я обычно отклоняюсь от этого правила, это когда я оборачиваю другую функцию, например:
def foo(bar):
print 'Bar:', bar
def baz(qux, *args):
print 'Qux:', qux
foo(*args)
Я бы никогда не сделал это для такого простого примера, но предположим, foo
это функция из стороннего пакета вне моего контроля с множеством значений по умолчанию, аргументов ключевых слов и т. д. В этом случае я бы предпочел разбирать аргументы для анализа в Python, чем пытаться сам.
Лично я написал бы это как класс как:
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): ...
А потом в клиенте напиши:
from worker import Worker
w = Worker(iserver,login,password,proxyUser,proxyPass,proxyServer,service)
w.version_check()
w.version_get()
w.version_send()
Если вам действительно нужно писать функции с большим количеством аргументов, а не инкапсулировать это состояние в классе - что является более типичным для Pythonic способом сделать это - тогда рассмотрите тип данных namedtuple из последних версий Python. Он позволяет вам указать кортеж, в котором элементы могут быть адресованы по ключевому слову, и может создать очень чистый и элегантный код.
Есть много подходов, в зависимости от того, что представляют эти аргументы.
Если они являются просто набором аргументов (особенно если некоторые являются необязательными), используйте ключевые аргументы:
myargs = {'iserver':'server','login':'username','password':'Pa2230rd'} version_get(**myargs)
Если они представляют какую-то вещь со своим собственным состоянием, то используйте классы:
Если аргументы представляют одно состояние, которое изменяют ваши функции, тогда примите аргументы в конструкторе объектов и сделайте ваши
version_*
методы-функции этого класса: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()
Если у вас есть какой-то ресурс, эти аргументы представляют, что ваши функции просто используют, в этом случае используйте отдельный класс и предоставьте его в качестве параметра объекта для ваших функций:
class Connection(Object): def __init__(self, iserver, ...): self.iserver # etc myconn = Connection('iserver',...) version_check(myconn)
Скорее всего, это два разных ресурса и должно быть двух классов. В этом случае вы можете объединить эти подходы:
#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()
Возможно, ваши аргументы представляют более одного объекта (например, соединение и прозрачный прокси-объект). В этом случае попробуйте создать объект с наименьшими методами открытого интерфейса, такими как
version_*
будет необходимо и инкапсулировать состояние, представленное другими аргументами, используя состав объекта.Например, если у вас есть прокси-соединения, вы можете создать
Connection()
класс, который просто знает о сервере, логине и пароле, иConnectionProxy()
класс, который имеет все методыConnection
, но перенаправляет на другой объект подключения. Это позволяет вам отделитьproxy*
аргументы, а значит, что вашversion_*
функции могут не знать, используют ли они прокси или нет.
Если ваши аргументы являются просто состоянием и не имеют подходящих для них методов, рассмотрите возможность использования
namedtuple()
, Это будет действовать как более умный кортеж (включая распаковку кортежа, нарезку и т. Д.) И будет оказывать минимальное влияние на существующий код, но при этом будет проще в использовании.Connection = namedtuple('Connection', 'iserver login password etc') myconn = Connection('iserver', 'loginname', 'passw3rd') version_check(*myconn)
Вы можете создать экземпляр объекта или определить класс. например
файл 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)
файл 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