الحصول على الحجج الكلمة مرت بالفعل إلى طريقة بيثون

StackOverflow https://stackoverflow.com/questions/1408818

  •  05-07-2019
  •  | 
  •  

سؤال

وانا في حلم من طريقة بيثون مع وسائط الكلمة الصريحة:

def func(a=None, b=None, c=None):
    for arg, val in magic_arg_dict.items():   # Where do I get the magic?
        print '%s: %s' % (arg, val)

وأنا ترغب في الحصول على القاموس فقط تلك الحجج المتصل مرت فعلا في طريقة، تماما مثل **kwargs، ولكن أنا لا أريد المتصل لتكون قادرة على تمرير أي وسائط العشوائية القديمة، على عكس **kwargs.

>>> func(b=2)
b: 2
>>> func(a=3, c=5)
a: 3
c: 5

وهكذا: لا يوجد مثل هذا التعويذة؟ في حالتي، وأنا يحدث ليكون قادرا على مقارنة كل حجة ضد الافتراضي للعثور على تلك التي هي مختلفة، ولكن هذا النوع من ائقة ويحصل مملة عندما يكون لديك تسعة الحجج. للحصول على نقاط المكافأة، وتوفير التعويذة التي يمكن أن تقول لي حتى عندما يمر الطالب في حجة رئيسية تعيين قيمته الافتراضية:

>>> func(a=None)
a: None

والانيق!

تعديل: و(المفردات) توقيع وظيفة يجب أن تبقى على حالها. انها جزء من API العام، وقيمتها الأساسية للسائط رئيسية واضحة تكمن في القيمة وثائقي الخاصة بهم. فقط لجعل الامور مثيرة للاهتمام. :)

هل كانت مفيدة؟

المحلول

وأنا كانت مستوحاة من الخير الديكور خسر النظرية، وبعد ان لعب معه حول قليلا حتى جاء مع هذا:

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"، ولكن هذا هو النهج أبسط الذي يوضح الحل.

وط ط ط، بيثون هو لذيذ.

نصائح أخرى

وهنا هو الطريقة الأسهل والأبسط:

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 هنا .

واحتمال واحد:

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

وIOW، فإنه من السهل جدا للتأكد من أن مرت في أسماء ضمن مجموعة مقبول وغير ذلك رفع كل ما كنت تريد بيثون لرفع (TypeError، أعتقد ؛-). من السهل جدا أن تتحول إلى الديكور، راجع للشغل.

وهناك احتمال آخر:

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

وبجعل _sentinel كائن فريد من نوعه إزالة خطر أن المتصل قد يكون قصد تمرير None (أو غيرها من القيم الافتراضية غير فريد المتصل ربما يمكن أن تمر). هذا هو كل object() هو جيد ل، راجع للشغل: والحارس للغاية خفيفة الوزن، الفريد الذي لا يمكن ربما يكون الخلط عن طريق الخطأ مع أي كائن آخر (عند تحقق مع المشغل is)

وإما الحل الأفضل لمشاكل مختلفة قليلا.

وماذا عن استخدام الديكور للتحقق من صحة 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')

وهو الأسهل تحقيق ذلك مع نسخة واحدة من كائن الحراسة:

# Top of module, does not need to be exposed in __all__
missing = {}

# Function prototype
def myFunc(a = missing, b = missing, c = missing):
    if a is not missing:
        # User specified argument a
    if b is missing:
        # User did not specify argument b

والشيء الجميل في هذا النهج هو أنه، منذ أن كنت تستخدم "هو" المشغل، المتصل يمكن أن تمر على ديكت فارغة كقيمة حجة، ونحن سوف لا تزال التقاط أنهم لم أقصد أن تمر عليه . نحن أيضا تجنب الديكور سيئة بهذه الطريقة، والحفاظ على رمز لنا قليلا أكثر نظافة.

وهناك طرق ربما أفضل للقيام بذلك، ولكن هنا بلدي يأخذ:

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

وربما رفع خطأ إذا مروا أي وسائط *؟

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()))

وانها تريد ان تكون بسيطة لعامل ذلك إلى اتخاذ قائمة varnames أو وظيفة التوزيع العامة للاستخدام. فإنه لن يكون من الصعب جدا لجعل هذا في الديكور (الثعبان 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 بالفعل

.

ماجيك ليست هي الحل:

def funky(a=None, b=None, c=None):
    for name, value in [('a', a), ('b', b), ('c', c)]:
        print name, value
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top