【问题标题】:The right way to use **kwargs in Python在 Python 中使用 **kwargs 的正确方法
【发布时间】:2018-09-14 12:01:48
【问题描述】:

我查看了this question,但它并没有完全回答我的问题。 例如,我采用了一种简单的方法来打印我的姓名。

def call_me_by_name(first_name):
    print("Your name is {}".format(first_name))

后来,我意识到可选地,我还希望能够打印中间名和姓氏。我进行了以下更改以适应使用 **kwargs 的情况,担心将来我可能会为名称本身添加更多字段(例如第三、第四、第五名称等)

我决定使用 **kwargs

def call_me_by_name(first_name,**kwargs):

    middle_name = kwargs['middle_name'] if kwargs.get('middle_name') else ""
    last_name = kwargs['last_name'] if kwargs.get('last_name') else ""

    print("Your name is {} {} {}".format(first_name,middle_name,last_name))

我唯一担心的是,随着我继续实现对更多名称的支持,我最终会为每个可能会或可能不会出现的关键字参数编写一行代码。我想找到一个尽可能 Pythonic 的解决方案。有没有更好的方法来实现这一目标?

编辑 1

我想使用关键字参数,因为这只是一个示例程序。实际用例是通过文件解析。到目前为止,关键字参数将支持从

解析文件

1) 文件中的特定字节。
2) 文件中的特定行号。

在任何给定时间点只能设置这两个条件中的一个(因为不可能同时从文件中的特定字节偏移量和行号中读取。)但可能还有更多这样的条件将来的条件,例如从第一次出现的字符中解析文件等。可能有 10-20 种不同的此类条件,我的方法应该支持但只有一个条件会由调用者随时设置。除非没有其他选择,否则我不希望有 20-30 种不同的 IF 条件。

【问题讨论】:

  • 为什么不用* 解压dict?我的意思是您可以直接将 kwargs 格式设置为 ...format(*kwargs) ,但是您的格式化字符串必须只有占位符
  • @Take_Care_ 在 python 3.6 之前,这不能保证名称的顺序正确。如果名字不见了怎么办?
  • @PM 2Ring 感谢您的提及。是的,用户必须意识到这一点:P
  • @PM2Ring,我已经编辑了我的问题以更彻底地解释我的用例。
  • 如果只能设置一个选项,为什么不直接使用enum 或类似的?

标签: python keyword-argument


【解决方案1】:

你有两个不同的问题,有两种不同的 Pythonic 方式来回答这些问题。

1- 您首先担心的是,在格式化字符串时,您开始支持的参数越多,您就不想继续添加新行。解决这个问题的方法是使用defaultdict,这样当你不提供特定的关键字参数和str.format_map 时,你可以返回一个空字符串,它接受一个字典作为输入关键字参数的一种方式来格式化。这样,您只需要更新您的字符串和要打印的关键字参数:

from collections import defaultdict
def call_me_by_name(**kwargs):
    default_kwargs = defaultdict(str, kwargs)
    print("Your name is {first_name} {second_name} {third_name}".format_map(default_kwargs))

2- 另一方面,如果在回答您的第二个问题时,您希望根据关键字参数提供不同的行为,例如更改字符串的外观或提供不同的文件查找功能,而不使用 if 语句,您有添加不同的函数/方法并从这个通用函数/方法中调用它们。这里有两种方法:

OOP:

class FileLookup:

    def parse(self, **kwargs):
        return getattr(self, next(iter(kwargs)))(**kwargs)

    def line_number(self, line_number):
        print('parsing with a line number: {}'.format(line_number))

    def byte_position(self, byte_position):
        print('parsing with a byte position: {}'.format(byte_position))

fl = FileLookup()
fl.parse(byte_position=10)
fl.parse(line_number=10)

模块:

def line_number(line_number):
    print('parsing with a line number: {}'.format(line_number))

def byte_position(byte_position):
    print('parsing with a byte position: {}'.format(byte_position))

def parse(**kwargs):
    return globals()[next(iter(kwargs))](**kwargs)

parse(byte_position=29)
parse(line_number=29)

【讨论】:

    【解决方案2】:

    在我看来你最好不使用 kwargs 甚至不使用函数,你可以简单地做这样的事情:

    print("Your name is", " ".join([first_name, middle_name, last_name]))
    

    或者如果你确实想要一个函数:

    def call_me_by_name(*args):
         print("Your name is", " ".join(args))
    

    【讨论】:

    • 你可以让print做所有的工作,例如print("Your name is", *args)
    • @PM2Ring,确实更好。
    【解决方案3】:

    如果您的函数特别关心关键字参数,那么这可能不是正确的工具。在这种情况下,您可以使用默认参数获得相同的效果:

    def call_me_by_name(first_name, middle_name="", last_name=""):
        print("Your name is {} {} {}".format(first_name,middle_name,last_name))
    

    当您想要一种“抓包”选项时,它会更好。例如

    def configure(**kwargs):
        if 'color' in kwargs:
            set_color(kwargs['color'])
    

    等等

    【讨论】:

    • 我已经编辑了我的帖子并解释了实际用例。
    【解决方案4】:

    您可以通过以下方式简化它:

    middle_name = kwargs.get('middle_name', '')
    

    【讨论】:

      【解决方案5】:

      我会把它作为答案发布:

      您可以立即将 kwargs 值解压缩为格式函数,如下所示:

      "Your name is {} {} {}".format(first_name , *kwargs)
      

      但正如用户 @PM 2Ring 提到的,您必须注意,这并不能保证名称的顺序正确。

      【讨论】:

        【解决方案6】:

        我认为你的 call_me_by_name 不是 **kwargs 的好例子。但是,如果您想避免省略一些奇异的、未考虑的名称字段,call_me_by_name 可能如下所示:

        def call_me_by_name(first_name, last_name, middle_name='', **kwargs):
            s = "Your name is {} {} {}".format(first_name,middle_name,last_name)
            if kwargs:
                s += " (" + ", ".join(["{}: {}".format(k,v) for k,v in kwargs.items()]) + ")"
            print(s)
        

        测试:

        name = {'first_name': 'Henry', 'last_name': 'Ford', 'ordinal': 'II', 'nickname': 'Hank the Deuce'}
        call_me_by_name(**name)
        >>> Your name is Henry  Ford (ordinal: II, nickname: Hank the Deuce)
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2010-11-11
          • 1970-01-01
          • 2021-08-19
          • 2014-09-07
          • 1970-01-01
          相关资源
          最近更新 更多