2017-01-05 6 views
0

Пусть у меня есть два класса ...Как функция может принимать ** kwargs в качестве аргументов и распределять их между разными классами?

class A: 

    def __init__(self, *args, arg1="default1", arg2="default2"): 
     # Initialise class A 


class B: 

    def __init__(self, arg3="default3", arg4="default4"): 
     # Initialise class B 

Каждый класс имеет свои собственные ключевые аргументы, и один имеет позиционные аргументы.

Теперь предположим, что есть функция, которая создает экземпляр каждого из этих классов, используя свои собственные аргументы, чтобы сделать так:

def make_objs(*args, arg1="default1", arg2="default2", arg3="default3", arg4="default4"): 
    objA = ClassA(*args, arg1=arg1, arg2=arg2) 
    objB = ClassB(arg3=arg3, arg4=arg4) 

Здесь я вручную выделить ключевые аргументы, что функция, полученные в соответствующий класс , Это немного утомительно, но я должен дублировать ключевые слова в определении функции, а изменение классов будет означать изменение аргументов функции.

В идеале я хотел бы сделать что-то вроде этого:

def make_objs(*args, **kwargs): 
    objA = ClassA(*args, **kwargs) 
    objB = ClassB(**kwargs) 

Где каждый класс будет принимать все именованные аргументы и извлекать только те, которые имеют отношение к ней. Это не то, что, на самом деле, сделал бы этот код, он выбросит исключение, потому что ClassA не ожидает аргумента arg3.

Есть ли все равно для этого? Какой-то способ сделать функцию в качестве аргумента **kwargs и определить, какие аргументы могут перейти в какой класс?

+0

Как насчет для 'constructor' аргументов в' классе A' вы добавляете '* arg' и' ** kwarg' после 'arg1' и' arg2'? То же самое относится к классу B. –

+0

Вопрос: Когда вы собираетесь распаковать '* args' в' classA'? Можете ли вы предоставить полное определение вашего 'constructor' для' classA'? –

+0

Функция на самом деле: https://github.com/samirelanduk/quickplots/blob/2.0/quickplots/quick.py#L4 –

ответ

0

Если вы добавите *arg и **kwargs в __init__ метод ваших занятий, вы можете достичь ожидаемого поведения. Как и в приведенной ниже exaple:

class A(object): 

    def __init__(self, a, b, *arg, **kwargs): 
     self.a = a 
     self.b = b 

class B(object): 

    def __init__(self, c, d, *arg, **kwargs): 
     self.c = c 
     self.d = d 

def make_objs(**kwargs): 
    objA = A(**kwargs) 
    objB = B(**kwargs) 

make_objs(a='apple', b='ball', c='charlie', d='delta') 

Но нюанс в том, что если вы печатаете objA.c и objA.d вернет charlie и delta, как прошел в make_objs параметров.

+0

Извините, я должен был указать, что я уже использую * args для classA. classA __init__ is '(self, * args, ** kwargs)' и функция принимает '(* args, ** kwargs)'. –

+0

Можете ли вы снова обновить свой вопрос с помощью этой информации? Благодарю. Также вы можете уточнить, чего вы ожидали достичь. Возможно, я не понимаю вашего вопроса. –

+0

Спасибо, я только что сделал. –

0
class A: 
    def __init__(self, *args, a1 = 'd1', a2 = 'd2'): 
     self.a1 = a1 
     self.a2 = a2 
class B: 
    def __init__(self, a3 = 'd3', a4 = 'd4'): 
     self.a3 = a3 
     self.a4 = a4 

Используйте inpect.signature, чтобы получить подписи вызова затем процеживают kwargs до создания объектов.

from inspect import signature 
from inspect import Parameter 

sig_a = signature(A) 
sig_b = signature(B) 

def f(*args, **kwargs): 
    d1 = {} 
    d2 = {} 
    for k, v in kwargs.items(): 
     try: 
      if sig_a.parameters[k].kind in (Parameter.KEYWORD_ONLY, 
              Parameter.POSITIONAL_OR_KEYWORD, 
              Parameter.VAR_KEYWORD): 
       d1[k] = v 
     except KeyError: 
      pass 
     try: 
      if sig_b.parameters[k].kind in (Parameter.KEYWORD_ONLY, 
              Parameter.POSITIONAL_OR_KEYWORD, 
              Parameter.VAR_KEYWORD): 
       d2[k] = v 
     except KeyError: 
      pass 
    return (A(args, **d1), B(**d2)) 

d = {'a1':1, 'a2':2, 'a3':3, 'a4':4} 
x, y = f(2, **d) 

>>> x.a1 
1 
>>> x.a2 
2 
>>> y.a3 
3 
>>> y.a4 
4 
>>> 

проверяет, чтобы увидеть, если параметр является ключевым словом параметр может быть излишним.

1

Чтобы избежать повторного набора текста, вы можете создать служебную функцию, которая использует модуль inspect для проверки вызывающей последовательности задействованных функций/методов.

Вот пример использования его кода. Функция полезности является один с именем get_kwarg_names:

from inspect import signature, Parameter 

class ClassA: 
    def __init__(self, *args, arg1="default1", arg2="default2"): 
     print('ClassA.__init__(): ' 
       '*args={}, arg1={!r}, arg2={!r}'.format(args, arg1, arg2)) 

class ClassB: 
    def __init__(self, arg3="default3", arg4="default4"): 
     print('ClassB.__init__(): arg3={!r}, arg4={!r}'.format(arg3, arg4)) 

def get_kwarg_names(function): 
    """ Return a list of keyword argument names function accepts. """ 
    sig = signature(function) 
    keywords = [] 
    for param in sig.parameters.values(): 
     if(param.kind == Parameter.KEYWORD_ONLY or 
      (param.kind == Parameter.POSITIONAL_OR_KEYWORD and 
       param.default != Parameter.empty)): 
      keywords.append(param.name) 
    return keywords 

# Sample usage of utility function above. 
def make_objs(*args, arg1="default1", arg2="default2", 
        arg3="default3", arg4="default4"): 

    local_namespace = locals() 
    classA_kwargs = {keyword: local_namespace[keyword] 
         for keyword in get_kwarg_names(ClassA.__init__)} 
    objA = ClassA(*args, **classA_kwargs) 

    classB_kwargs = {keyword: local_namespace[keyword] 
         for keyword in get_kwarg_names(ClassB.__init__)} 
    objB = ClassB(**classB_kwargs) 

make_objs(1, 2, arg1="val1", arg2="val2", arg4="val4") 

Выход:

ClassA.__init__(): *args=(1, 2), arg1='val1', arg2='val2' 
ClassB.__init__(): arg3='default3', arg4='val4' 
+0

Мне нравится это лучше, у меня возникло искушение сделать его функцией, которая вернула действительные параметры, но не обошла ее. – wwii

+0

Хорошо. Я попытался сделать что-то довольно общее, поэтому его можно было легко модифицировать при необходимости и повторно использовать, если это необходимо. Однако обратите внимание, что это требует, чтобы аргументы ключевого слова были уникальными. Если мой ответ решает вашу проблему, пожалуйста, подумайте о его принятии. – martineau