Python 函数中采用单个对象或可迭代对象的参数名称

发布于 2024-09-19 03:14:28 字数 334 浏览 8 评论 0原文

我的代码中有一些函数接受对象或可迭代对象作为输入。我被教导要为所有事物使用有意义的名称,但我不知道如何在这里遵守。我应该如何称呼可以是单个对象或可迭代对象的参数?我提出了两个想法,但我不喜欢其中任何一个:

  1. FooOrManyFoos - 这表达了正在发生的事情,但我可以想象不习惯它的人可能难以理解它的含义马上
  2. param - 一些通用名称。这清楚地表明它可以是多种内容,但没有解释该参数的用途。

通常,我将对象的可迭代对象称为单个对象的复数形式。我知道这可能看起来有点强迫性,但 Python 应该(除其他外)注重可读性。

I have some functions in my code that accept either an object or an iterable of objects as input. I was taught to use meaningful names for everything, but I am not sure how to comply here. What should I call a parameter that can a sinlge object or an iterable of objects? I have come up with two ideas, but I don't like either of them:

  1. FooOrManyFoos - This expresses what goes on, but I could imagine that someone not used to it could have trouble understanding what it means right away
  2. param - Some generic name. This makes clear that it can be several things, but does explain nothing about what the parameter is used for.

Normally I call iterables of objects just the plural of what I would call a single object. I know this might seem a little bit compulsive, but Python is supposed to be (among others) about readability.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

东走西顾 2024-09-26 03:14:28

我的代码中有一些函数接受对象或可迭代对象作为输入。

这是一件非常特殊的事情,而且往往是非常糟糕的事情。这是完全可以避免的。

即,调用此函数时传递 [foo] 而不是 foo。

唯一可以证明这样做的合理性是当(1)您有一个需要一种形式(可迭代或单例)的已安装软件基础并且(2)您必须扩展它以支持其他用例时。所以。 在扩展具有现有代码库的现有函数时执行此操作。

如果这是新开发,请勿这样做。

我提出了两个想法,但我都不喜欢其中一个:

[只有两个?]

FooOrManyFoos - 这表达了正在发生的事情,但我可以想象不习惯它的人可能很难立即理解它的含义

什么?您是说您没有提供其他文件,也没有提供其他培训?没有支持?没有建议吗?谁是“不习惯的人”?与他们交谈。不要假设或想象有关他们的事情。

另外,请勿使用前导大写名称。

param - 一些通用名称。这清楚地表明它可以是多种内容,但没有解释该参数的用途。

糟糕的。绝不。做。这。

我在 Python 库中查找了示例。大多数执行此操作的函数都有简单的描述。

http://docs.python.org/library/functions.html#isinstance

isinstance(object, classinfo)

他们称之为“classinfo”,它可以是一个类或类的元组。

你也可以这样做。

必须考虑常见用例和例外情况。遵循 80/20 规则。

  1. 80% 的情况下,您可以将其替换为可迭代对象,而不会出现此问题。

  2. 在剩下的 20% 的情况下,您拥有围绕某个假设(可迭代或单个项目)构建的软件安装基础,并且您需要添加其他情况。不要更改名称,只需更改文档即可。如果它曾经说“foo”,它仍然说“foo”,但你让它接受“foo's”的可迭代,而不对参数进行任何更改。如果它曾经说“foo_list”或“foo_iter”,那么它仍然说“foo_list”或“foo_iter”,但它会悄悄地容忍单例而不破坏。

    • 80% 的代码是遗留代码(“foo”或“foo_list”)

    • 20% 的代码是新功能(“foo”可以是可迭代的,或者“foo_list”可以是单个对象。)

I have some functions in my code that accept either an object or an iterable of objects as input.

This is a very exceptional and often very bad thing to do. It's trivially avoidable.

i.e., pass [foo] instead of foo when calling this function.

The only time you can justify doing this is when (1) you have an installed base of software that expects one form (iterable or singleton) and (2) you have to expand it to support the other use case. So. You only do this when expanding an existing function that has an existing code base.

If this is new development, Do Not Do This.

I have come up with two ideas, but I don't like either of them:

[Only two?]

FooOrManyFoos - This expresses what goes on, but I could imagine that someone not used to it could have trouble understanding what it means right away

What? Are you saying you provide NO other documentation, and no other training? No support? No advice? Who is the "someone not used to it"? Talk to them. Don't assume or imagine things about them.

Also, don't use Leading Upper Case Names.

param - Some generic name. This makes clear that it can be several things, but does explain nothing about what the parameter is used for.

Terrible. Never. Do. This.

I looked in the Python library for examples. Most of the functions that do this have simple descriptions.

http://docs.python.org/library/functions.html#isinstance

isinstance(object, classinfo)

They call it "classinfo" and it can be a class or a tuple of classes.

You could do that, too.

You must consider the common use case and the exceptions. Follow the 80/20 rule.

  1. 80% of the time, you can replace this with an iterable and not have this problem.

  2. In the remaining 20% of the cases, you have an installed base of software built around an assumption (either iterable or single item) and you need to add the other case. Don't change the name, just change the documentation. If it used to say "foo" it still says "foo" but you make it accept an iterable of "foo's" without making any change to the parameters. If it used to say "foo_list" or "foo_iter", then it still says "foo_list" or "foo_iter" but it will quietly tolerate a singleton without breaking.

    • 80% of the code is the legacy ("foo" or "foo_list")

    • 20% of the code is the new feature ("foo" can be an iterable or "foo_list" can be a single object.)

衣神在巴黎 2024-09-26 03:14:28

我想我参加聚会有点晚了,但我很惊讶没有人推荐装饰师。

def withmany(f):
    def many(many_foos):
        for foo in many_foos:
            yield f(foo)
    f.many = many
    return f

@withmany
def process_foo(foo):
    return foo + 1


processed_foo = process_foo(foo)

for processed_foo in process_foo.many(foos):
    print processed_foo

我在 Alex Martelli 的一篇帖子中看到了类似的模式,但我不记得该链接了。

I guess I'm a little late to the party, but I'm suprised that nobody suggested a decorator.

def withmany(f):
    def many(many_foos):
        for foo in many_foos:
            yield f(foo)
    f.many = many
    return f

@withmany
def process_foo(foo):
    return foo + 1


processed_foo = process_foo(foo)

for processed_foo in process_foo.many(foos):
    print processed_foo

I saw a similar pattern in one of Alex Martelli's posts but I don't remember the link off hand.

怪我闹别瞎闹 2024-09-26 03:14:28

听起来您正在为代码的丑陋而苦恼,例如:

def ProcessWidget(widget_thing):
  # Infer if we have a singleton instance and make it a
  # length 1 list for consistency
  if isinstance(widget_thing, WidgetType):
    widget_thing = [widget_thing]

  for widget in widget_thing:
    #...

我的建议是避免超载您的界面来处理两种不同的情况。我倾向于编写有利于重用和清晰命名方法的代码,而不是巧妙动态使用参数:

def ProcessOneWidget(widget):
  #...

def ProcessManyWidgets(widgets):
  for widget in widgets:
    ProcessOneWidget(widget)

通常,我从这个简单的模式开始,但是当有效率时,有机会优化“许多”情况抵消额外的代码复杂性和部分功能重复。如果这一约定看起来过于冗长,可以选择“ProcessWidget”和“ProcessWidgets”之类的名称,尽管两者之间的区别是一个容易错过的字符。

It sounds like you're agonizing over the ugliness of code like:

def ProcessWidget(widget_thing):
  # Infer if we have a singleton instance and make it a
  # length 1 list for consistency
  if isinstance(widget_thing, WidgetType):
    widget_thing = [widget_thing]

  for widget in widget_thing:
    #...

My suggestion is to avoid overloading your interface to handle two distinct cases. I tend to write code that favors re-use and clear naming of methods over clever dynamic use of parameters:

def ProcessOneWidget(widget):
  #...

def ProcessManyWidgets(widgets):
  for widget in widgets:
    ProcessOneWidget(widget)

Often, I start with this simple pattern, but then have the opportunity to optimize the "Many" case when there are efficiencies to gain that offset the additional code complexity and partial duplication of functionality. If this convention seems overly verbose, one can opt for names like "ProcessWidget" and "ProcessWidgets", though the difference between the two is a single easily missed character.

栩栩如生 2024-09-26 03:14:28

您可以使用 *args magic (varargs) 使您的参数始终可迭代。

将单个项目或多个已知项目作为普通函数参数传递,如 func(arg1, arg2, ...) 并传递前面带有星号的可迭代参数,如 func(*args)强>

示例:

# magic *args function
def foo(*args):
    print args

# many ways to call it
foo(1)
foo(1, 2, 3)

args1 = (1, 2, 3)
args2 = [1, 2, 3]
args3 = iter((1, 2, 3))

foo(*args1)
foo(*args2)
foo(*args3)

You can use *args magic (varargs) to make your params always be iterable.

Pass a single item or multiple known items as normal function args like func(arg1, arg2, ...) and pass iterable arguments with an asterisk before, like func(*args)

Example:

# magic *args function
def foo(*args):
    print args

# many ways to call it
foo(1)
foo(1, 2, 3)

args1 = (1, 2, 3)
args2 = [1, 2, 3]
args3 = iter((1, 2, 3))

foo(*args1)
foo(*args2)
foo(*args3)
赤濁 2024-09-26 03:14:28

您能以非常高级的方式命名您的参数吗?阅读代码的人更感兴趣的是了解参数代表什么(“客户端”)而不是参数的类型(“list_of_tuples”);类型可以在函数文档字符串中定义,这是一件好事,因为它将来可能会改变(类型有时是实现细节)。

Can you name your parameter in a very high-level way? people who read the code are more interested in knowing what the parameter represents ("clients") than what their type is ("list_of_tuples"); the type can be defined in the function documentation string, which is a good thing since it might change, in the future (the type is sometimes an implementation detail).

云归处 2024-09-26 03:14:28

我会做一件事,

def myFunc(manyFoos):
    if not type(manyFoos) in (list,tuple):
        manyFoos = [manyFoos]
    #do stuff here

这样你就不用再担心它的名字了。

在函数中,您应该尝试实现 1 个操作,接受相同的参数类型并返回相同的类型。

您可以拥有 2 个函数,而不是用 if 填充函数。

I would do 1 thing,

def myFunc(manyFoos):
    if not type(manyFoos) in (list,tuple):
        manyFoos = [manyFoos]
    #do stuff here

so then you don't need to worry anymore about its name.

in a function you should try to achieve to have 1 action, accept the same parameter type and return the same type.

Instead of filling the functions with ifs you could have 2 functions.

给妤﹃绝世温柔 2024-09-26 03:14:28

由于您并不关心获得的迭代器类型,因此您可以尝试使用 iter() 获取参数的迭代器。如果 iter() 引发 TypeError 异常,则该参数不可迭代,因此您可以创建一个可迭代的列表或元组,并且 Bob 是您的叔叔。

def doIt(foos):
    try:
        iter(foos)
    except TypeError:
        foos = [foos]
    for foo in foos:
        pass    # do something here

这种方法的唯一问题是 foo 是否是一个字符串。字符串是可迭代的,因此传入单个字符串而不是字符串列表将导致迭代字符串中的字符。如果这是一个问题,您可以添加一个 if 测试。此时,样板代码变得冗长,因此我将其分解为自己的函数。

def iterfy(iterable):
    if isinstance(iterable, basestring):
        iterable = [iterable]
    try:
        iter(iterable)
    except TypeError:
        iterable = [iterable]
    return iterable

def doIt(foos):
    for foo in iterfy(foos):
        pass    # do something

与某些回答不同,我喜欢这样做,因为它消除了调用者在使用 API 时可能出错的一件事。 “对你所创造的东西保持保守,但对你接受的东西保持自由。”

为了回答您最初的问题,即您应该为参数命名什么,即使您将接受单个项目,我仍然会选择“foos”,因为您的意图是接受一个列表。如果它不可迭代,那么从技术上来说这是一个错误,尽管您将为调用者纠正这个错误,因为只处理一项可能正是他们想要的。另外,如果调用者认为他们必须传入一个可迭代的项目,那么,这当然可以很好地工作并且需要很少的语法,那么为什么要担心纠正他们的误解呢?

Since you don't care exactly what kind of iterable you get, you could try to get an iterator for the parameter using iter(). If iter() raises a TypeError exception, the parameter is not iterable, so you then create a list or tuple of the one item, which is iterable and Bob's your uncle.

def doIt(foos):
    try:
        iter(foos)
    except TypeError:
        foos = [foos]
    for foo in foos:
        pass    # do something here

The only problem with this approach is if foo is a string. A string is iterable, so passing in a single string rather than a list of strings will result in iterating over the characters in a string. If this is a concern, you could add an if test for it. At this point it's getting wordy for boilerplate code, so I'd break it out into its own function.

def iterfy(iterable):
    if isinstance(iterable, basestring):
        iterable = [iterable]
    try:
        iter(iterable)
    except TypeError:
        iterable = [iterable]
    return iterable

def doIt(foos):
    for foo in iterfy(foos):
        pass    # do something

Unlike some of those answering, I like doing this, since it eliminates one thing the caller could get wrong when using your API. "Be conservative in what you generate but liberal in what you accept."

To answer your original question, i.e. what you should name the parameter, I would still go with "foos" even though you will accept a single item, since your intent is to accept a list. If it's not iterable, that is technically a mistake, albeit one you will correct for the caller since processing just the one item is probably what they want. Also, if the caller thinks they must pass in an iterable even of one item, well, that will of course work fine and requires very little syntax, so why worry about correcting their misapprehension?

微凉徒眸意 2024-09-26 03:14:28

我会用一个名称来解释参数可以是一个实例或实例列表。说one_or_more_Foo_objects。我发现它比平淡的 param 更好。

I would go with a name explaining that the parameter can be an instance or a list of instances. Say one_or_more_Foo_objects. I find it better than the bland param.

醉城メ夜风 2024-09-26 03:14:28

我现在正在开发一个相当大的项目,我们正在传递地图并仅调用我们的参数map。映射内容根据所调用的函数而有所不同。这可能不是最好的情况,但我们在地图上重用了很多相同的代码,因此复制和粘贴更容易。

我想说的是,你应该根据它的用途来命名,而不是根据它的名称来命名。另外,请注意,您不能在不可迭代的对象上调用 use in

I'm working on a fairly big project now and we're passing maps around and just calling our parameter map. The map contents vary depending on the function that's being called. This probably isn't the best situation, but we reuse a lot of the same code on the maps, so copying and pasting is easier.

I would say instead of naming it what it is, you should name it what it's used for. Also, just be careful that you can't call use in on a not iterable.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文