Получение аргументов ключевого слова на самом деле передало методу Python

только преимуществом дополнительных методов является удобочитаемость кода. Вот именно.

Дополнительные методы позволяют Вам делать это:

foo.bar();

вместо этого:

Util.bar(foo);

Теперь существует много вещей в C#, которые похожи на это. Другими словами, существует много функций в C#, которые кажутся тривиальными и не обладают большим преимуществом в и себя. Однако, после того как Вы начинаете сочетать эти функции вместе, Вы начинаете видеть что-то просто немного большее, чем сумма его частей. Преимущества LINQ значительно от дополнительных методов как запросы LINQ были бы почти нечитабельны без них. LINQ был бы возможен без дополнительных методов, но не практичный.

Дополнительные методы много похожи на частичные классы C#. Собой они не очень полезны и кажутся тривиальными. Но когда Вы начинаете работать с классом, которому нужен сгенерированный код, частичные классы начинают иметь намного больше смысла.

23
задан Cœur 3 April 2018 в 10:39
поделиться

7 ответов

Я был вдохновлен добротой декоратора в теории потерь, и после экспериментов с он ненадолго придумал это:

def actual_kwargs():
    """
    Decorator that provides the wrapped function with an attribute 'actual_kwargs'
    containing just those keyword arguments actually passed in to the function.
    """
    def decorator(function):
        def inner(*args, **kwargs):
            inner.actual_kwargs = kwargs
            return function(*args, **kwargs)
        return inner
    return decorator


if __name__ == "__main__":

    @actual_kwargs()
    def func(msg, a=None, b=False, c='', d=0):
        print msg
        for arg, val in sorted(func.actual_kwargs.iteritems()):
            print '  %s: %s' % (arg, val)

    func("I'm only passing a", a='a')
    func("Here's b and c", b=True, c='c')
    func("All defaults", a=None, b=False, c='', d=0)
    func("Nothin'")
    try:
        func("Invalid kwarg", e="bogon")
    except TypeError, err:
        print 'Invalid kwarg\n  %s' % err

Что напечатало это:

I'm only passing a
  a: a
Here's b and c
  b: True
  c: c
All defaults
  a: None
  b: False
  c: 
  d: 0
Nothin'
Invalid kwarg
  func() got an unexpected keyword argument 'e'

Я доволен этим. Более гибкий подход - передать имя атрибута, который вы хотите использовать, в декоратор, вместо того, чтобы жестко кодировать его как «actual_kwargs», но это самый простой подход, который иллюстрирует решение.

Ммм, Python вкусный .

25
ответ дан 29 November 2019 в 01:38
поделиться

Вот самый простой и простой способ:

def func(a=None, b=None, c=None):
    args = locals().copy()
    print args

func(2, "egg")

Это даст результат: {'a': 2, 'c': None, 'b': 'egg'} . Причина, по которой args должен быть копией словаря locals , заключается в том, что словари изменяемы, поэтому, если вы создали какие-либо локальные переменные в этой функции, args будет содержать все локальные переменные и их значения, а не только аргументы.

Дополнительная документация по встроенной функции locals здесь .

16
ответ дан 29 November 2019 в 01:38
поделиться

Одна возможность:

def f(**kw):
  acceptable_names = set('a', 'b', 'c')
  if not (set(kw) <= acceptable_names):
    raise WhateverYouWantException(whatever)
  ...proceed...

IOW, очень легко проверить, что переданные имена находятся в допустимом наборе, и в противном случае повысить все, что вы хотите, чтобы Python поднял (TypeError, я думаю ;-). Между прочим, довольно легко превратить в декоратора.

Другая возможность:

_sentinel = object():
def f(a=_sentinel, b=_sentinel, c=_sentinel):
   ...proceed with checks `is _sentinel`...

путем создания уникального объекта _sentinel вы устраняете риск того, что вызывающий может случайно передать None (или другие неуникальные значения по умолчанию, которые вызывающий может передать). Это все, для чего подходит object () , кстати: чрезвычайно легкий, уникальный дозорный, который невозможно случайно спутать с каким-либо другим объектом (когда вы проверяете с помощью оператора , это оператор) .

Любое решение предпочтительнее для немного разных задач.

5
ответ дан 29 November 2019 в 01:38
поделиться

Как насчет использования декоратора для проверки входящих kwargs?

def validate_kwargs(*keys):
    def entangle(f):
        def inner(*args, **kwargs):
            for key in kwargs:
                if not key in keys:
                    raise ValueError("Received bad kwarg: '%s', expected: %s" % (key, keys))
            return f(*args, **kwargs)
        return inner
    return entangle

###

@validate_kwargs('a', 'b', 'c')
def func(**kwargs):
   for arg,val in kwargs.items():
       print arg, "->", val

func(b=2)
print '----'
func(a=3, c=5)
print '----'
func(d='not gonna work')

Дает следующий результат:

b -> 2
----
a -> 3
c -> 5
----
Traceback (most recent call last):
  File "kwargs.py", line 20, in <module>
    func(d='not gonna work')
  File "kwargs.py", line 6, in inner
    raise ValueError("Received bad kwarg: '%s', expected: %s" % (key, keys))
ValueError: Received bad kwarg: 'd', expected: ('a', 'b', 'c')
2
ответ дан 29 November 2019 в 01:38
поделиться

Возможно, есть способы сделать это лучше, но вот мой вариант:

def CompareArgs(argdict, **kwargs):
    if not set(argdict.keys()) <= set(kwargs.keys()):
        # not <= may seem weird, but comparing sets sometimes gives weird results.
        # set1 <= set2 means that all items in set 1 are present in set 2
        raise ValueError("invalid args")

def foo(**kwargs):
    # we declare foo's "standard" args to be a, b, c
    CompareArgs(kwargs, a=None, b=None, c=None)
    print "Inside foo"


if __name__ == "__main__":
    foo(a=1)
    foo(a=1, b=3)
    foo(a=1, b=3, c=5)
    foo(c=10)
    foo(bar=6)

и его вывод:

Inside foo
Inside foo
Inside foo
Inside foo
Traceback (most recent call last):
  File "a.py", line 18, in 
    foo(bar=6)
  File "a.py", line 9, in foo
    CompareArgs(kwargs, a=None, b=None, c=None)
  File "a.py", line 5, in CompareArgs
    raise ValueError("invalid args")
ValueError: invalid args

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

0
ответ дан 29 November 2019 в 01:38
поделиться

Возможно, возникнет ошибка, если они передадут какие-либо * args?

def func(*args, **kwargs):
  if args:
    raise TypeError("no positional args allowed")
  arg1 = kwargs.pop("arg1", "default")
  if kwargs:
    raise TypeError("unknown args " + str(kwargs.keys()))

Было бы просто учесть это в использовании списка имен переменных или общей функции синтаксического анализа. Не было бы слишком сложно превратить это в декоратор (python 3.1):

def OnlyKwargs(func):
  allowed = func.__code__.co_varnames
  def wrap(*args, **kwargs):
    assert not args
    # or whatever logic you need wrt required args
    assert sorted(allowed) == sorted(kwargs)
    return func(**kwargs)

Примечание: я не уверен, насколько хорошо это будет работать с уже обернутыми функциями или функциями, имеющими * args или ** kwargs уже.

0
ответ дан 29 November 2019 в 01:38
поделиться

Magic - это не ответ:

def funky(a=None, b=None, c=None):
    for name, value in [('a', a), ('b', b), ('c', c)]:
        print name, value
0
ответ дан 29 November 2019 в 01:38
поделиться
Другие вопросы по тегам:

Похожие вопросы: