Лучший подход для передачи и обработки аргументов в функции

После некоторого прочтения я столкнулся с двумя разными способами передачи списка аргументов функции. Я прочитал некоторые указания. Вот что я понял до сих пор:

Актуальный код:

файл 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. Он позволяет вам указать кортеж, в котором элементы могут быть адресованы по ключевому слову, и может создать очень чистый и элегантный код.

Есть много подходов, в зависимости от того, что представляют эти аргументы.

  1. Если они являются просто набором аргументов (особенно если некоторые являются необязательными), используйте ключевые аргументы:

    myargs = {'iserver':'server','login':'username','password':'Pa2230rd'}
    version_get(**myargs)
    
  2. Если они представляют какую-то вещь со своим собственным состоянием, то используйте классы:

    1. Если аргументы представляют одно состояние, которое изменяют ваши функции, тогда примите аргументы в конструкторе объектов и сделайте ваши 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()
      
    2. Если у вас есть какой-то ресурс, эти аргументы представляют, что ваши функции просто используют, в этом случае используйте отдельный класс и предоставьте его в качестве параметра объекта для ваших функций:

      class Connection(Object):
          def __init__(self, iserver, ...):
              self.iserver # etc
      
      myconn = Connection('iserver',...)
      
      version_check(myconn)
      
    3. Скорее всего, это два разных ресурса и должно быть двух классов. В этом случае вы можете объединить эти подходы:

      #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. Возможно, ваши аргументы представляют более одного объекта (например, соединение и прозрачный прокси-объект). В этом случае попробуйте создать объект с наименьшими методами открытого интерфейса, такими как version_* будет необходимо и инкапсулировать состояние, представленное другими аргументами, используя состав объекта.

      Например, если у вас есть прокси-соединения, вы можете создать Connection() класс, который просто знает о сервере, логине и пароле, и ConnectionProxy() класс, который имеет все методы Connection, но перенаправляет на другой объект подключения. Это позволяет вам отделить proxy* аргументы, а значит, что ваш version_* функции могут не знать, используют ли они прокси или нет.

  3. Если ваши аргументы являются просто состоянием и не имеют подходящих для них методов, рассмотрите возможность использования 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
Другие вопросы по тегам