I want to refactor a big Python function into smaller ones. For example, consider this following code snippet:
我想将一个大的Python函数重构为较小的函数。例如,请考虑以下代码段:
x = x1 + x2 + x3 + x4 + x5 + x6 + x7 + x8 + x9
Of course, this is a trivial example. In practice, the code is more complex. My point is that it contains many local-scope variables that would have to be passed to the extracted function, which could look like:
当然,这是一个微不足道的例子。实际上,代码更复杂。我的观点是它包含许多必须传递给提取函数的局部范围变量,它们可能如下所示:
def mysum(x1, x2, x3, x4, x5, x6, x7, x8, x9):
x = x1 + x2 + x3 + x4 + x5 + x6 + x7 + x8 + x9
return x
The problem is that pylint would trigger a warning about too many arguments. I could avoid the warning by doing something like:
问题是pylint会触发关于太多参数的警告。做以下事情可以避免警告:
def mysum(d):
x1 = d['x1']
x2 = d['x2']
...
x9 = d['x9']
x = x1 + x2 + x3 + x4 + x5 + x6 + x7 + x8 + x9
return x
def mybigfunction():
...
d = {}
d['x1'] = x1
...
d['x9'] = x9
x = mysum(d)
but this approach loos ugly to me, it requires writing a lot of code that is even redundant.
但是这种方法对我来说太丑了,它需要编写很多甚至是多余的代码。
Is there a better way to do it?
有没有更好的方法呢?
8 个解决方案
#1
70
First, one of Perlis's epigrams:
首先,玻璃市的一个箴言:
"If you have a procedure with 10 parameters, you probably missed some."
“如果你有一个包含10个参数的程序,你可能会错过一些。”
Some of the 10 arguments are presumably related. Group them into an object, and pass that instead.
10个论点中的一些可能是相关的。将它们分组为一个对象,然后传递它。
Making an example up, because there's not enough information in the question to answer directly:
举个例子,因为问题中没有足够的信息可以直接回答:
class PersonInfo(object):
def __init__(self, name, age, iq):
self.name = name
self.age = age
self.iq = iq
Then your 10 argument function:
然后你的10个参数函数:
def f(x1, x2, name, x3, iq, x4, age, x5, x6, x7):
...
becomes:
def f(personinfo, x1, x2, x3, x4, x5, x6, x7):
...
and the caller changes to:
并且来电者更改为:
personinfo = PersonInfo(name, age, iq)
result = f(personinfo, x1, x2, x3, x4, x5, x6, x7)
#2
28
Do you want a better way to pass the arguments or just a way to stop pylint
from giving you a hard time? If the latter, I seem to recall that you could stop the nagging by putting pylint
-controlling comments in your code along the lines of:
你想要一个更好的方法来传递参数或只是一种方法来阻止pylint给你带来困难吗?如果是后者,我似乎记得你可以通过在你的代码中添加pylint控制注释来阻止唠叨:
#pylint: disable-msg=R0913
or:
#pylint: disable-msg=too-many-arguments
remembering to turn them back on as soon as practicable.
记得在切实可行的情况下尽快将它们重新打开。
In my opinion, there's nothing inherently wrong with passing a lot of arguments and solutions advocating wrapping them all up in some container argument don't really solve any problems, other than stopping pylint
from nagging you :-).
在我看来,传递大量论据并没有什么本质上的错误,并且主张在一些容器参数中包装它们的解决方案并没有真正解决任何问题,除了阻止pylint唠叨你:-)。
If you need to pass twenty arguments, then pass them. It may be that this is required because your function is doing too much and a re-factoring could assist there, and that's something you should look at. But it's not a decision we can really make unless we see what the 'real' code is.
如果你需要传递20个参数,那么传递它们。可能这是必需的,因为你的功能太多而且重新分解可以帮助那些,这是你应该看的东西。但除非我们看到“真正的”代码是什么,否则我们无法做出决定。
#3
17
You can easily change the maximum allowed number of arguments in pylint. Just open your pylintrc file (generate it if you don't already have one) and change:
您可以轻松更改pylint中允许的最大参数数量。只需打开你的pylintrc文件(如果你还没有它就生成它)并更改:
max-args=5
to:
max-args = 6 # or any value that suits you
max-args = 6#或任何适合您的值
From pylint's manual
来自pylint的手册
Specifying all the options suitable for your setup and coding standards can be tedious, so it is possible to use a rc file to specify the default values. Pylint looks for /etc/pylintrc and ~/.pylintrc. The --generate-rcfile option will generate a commented configuration file according to the current configuration on standard output and exit. You can put other options before this one to use them in the configuration, or start with the default values and hand tune the configuration.
指定适合您的设置和编码标准的所有选项可能很繁琐,因此可以使用rc文件指定默认值。 Pylint查找/ etc / pylintrc和〜/ .pylintrc。 --generate-rcfile选项将根据标准输出和退出的当前配置生成注释配置文件。您可以在此之前添加其他选项以在配置中使用它们,或者从默认值开始并手动调整配置。
#4
12
You could try using Python's variable arguments feature:
您可以尝试使用Python的变量参数功能:
def myfunction(*args):
for x in args:
# Do stuff with specific argument here
#5
7
Perhaps you could turn some of the arguments into member variables. If you need that much state a class sounds like a good idea to me.
也许您可以将一些参数转换为成员变量。如果你需要那么多的状态,那么对我来说,一堂课听起来不错。
#6
5
Simplify or break apart the function so that it doesn't require nine arguments (or ignore pylint, but dodges like the ones you're proposing defeat the purpose of a lint tool).
简化或分解函数,使其不需要九个参数(或忽略pylint,但像你提议的那样闪避一个lint工具的目的)。
EDIT: if it's a temporary measure, disable the warning for the particular function in question using a comment as described here: http://lists.logilab.org/pipermail/python-projects/2006-April/000664.html
编辑:如果这是一个临时措施,请使用此处所述的评论禁用相关特定功能的警告:http://lists.logilab.org/pipermail/python-projects/2006-April/000664.html
Later, you can grep for all of the disabled warnings.
稍后,您可以查看所有已禁用的警告。
#7
3
Comment about paxdiablo's reply - as I do not have enough reputation to comment there directly :-/
评论paxdiablo的回复 - 由于我没有足够的声誉直接在那里发表评论: - /
I do not like referring to the number, the sybolic name is much more expressive and avoid having to add a comment that could become obsolete over time.
我不喜欢引用数字,sybolic名称更具表现力,并且避免添加可能随着时间的推移而过时的评论。
So I'd rather do:
所以我宁愿这样做:
#pylint: disable-msg=too-many-arguments
And I would also recommend to not leave it dangling there: it will stay active until the file ends or it is disabled, whichever comes first.
而且我还建议不要将它悬挂在那里:它将保持活动状态,直到文件结束或被禁用,以先到者为准。
So better doing:
做得更好:
#pylint: disable-msg=too-many-arguments
code_which_would_trigger_the_msg
#pylint: enable-msg=too-many-arguments
I would also recommend enabling/disabling one single warning/error per line.
我还建议每行启用/禁用一个警告/错误。
#8
0
Python has some nice functional programming tools that are likely to fit your needs well. Check out lambda functions and map. Also, you're using dicts when it seems like you'd be much better served with lists. For the simple example you provided, try this idiom. Note that map would be better and faster but may not fit your needs:
Python有一些很好的函数编程工具,可能很好地满足您的需求。查看lambda函数和map。此外,当你似乎更好地使用列表时,你正在使用dicts。对于您提供的简单示例,请尝试此习语。请注意,地图会更好更快,但可能不符合您的需求:
def mysum(d):
s = 0
for x in d:
s += x
return s
def mybigfunction():
d = (x1, x2, x3, x4, x5, x6, x7, x8, x9)
return mysum(d)
You mentioned having a lot of local variables, but frankly if you're dealing with lists (or tuples), you should use lists and factor out all those local variables in the long run.
你提到有很多局部变量,但坦率地说,如果你正在处理列表(或元组),你应该使用列表并从长远来看将所有这些局部变量分解出来。
#1
70
First, one of Perlis's epigrams:
首先,玻璃市的一个箴言:
"If you have a procedure with 10 parameters, you probably missed some."
“如果你有一个包含10个参数的程序,你可能会错过一些。”
Some of the 10 arguments are presumably related. Group them into an object, and pass that instead.
10个论点中的一些可能是相关的。将它们分组为一个对象,然后传递它。
Making an example up, because there's not enough information in the question to answer directly:
举个例子,因为问题中没有足够的信息可以直接回答:
class PersonInfo(object):
def __init__(self, name, age, iq):
self.name = name
self.age = age
self.iq = iq
Then your 10 argument function:
然后你的10个参数函数:
def f(x1, x2, name, x3, iq, x4, age, x5, x6, x7):
...
becomes:
def f(personinfo, x1, x2, x3, x4, x5, x6, x7):
...
and the caller changes to:
并且来电者更改为:
personinfo = PersonInfo(name, age, iq)
result = f(personinfo, x1, x2, x3, x4, x5, x6, x7)
#2
28
Do you want a better way to pass the arguments or just a way to stop pylint
from giving you a hard time? If the latter, I seem to recall that you could stop the nagging by putting pylint
-controlling comments in your code along the lines of:
你想要一个更好的方法来传递参数或只是一种方法来阻止pylint给你带来困难吗?如果是后者,我似乎记得你可以通过在你的代码中添加pylint控制注释来阻止唠叨:
#pylint: disable-msg=R0913
or:
#pylint: disable-msg=too-many-arguments
remembering to turn them back on as soon as practicable.
记得在切实可行的情况下尽快将它们重新打开。
In my opinion, there's nothing inherently wrong with passing a lot of arguments and solutions advocating wrapping them all up in some container argument don't really solve any problems, other than stopping pylint
from nagging you :-).
在我看来,传递大量论据并没有什么本质上的错误,并且主张在一些容器参数中包装它们的解决方案并没有真正解决任何问题,除了阻止pylint唠叨你:-)。
If you need to pass twenty arguments, then pass them. It may be that this is required because your function is doing too much and a re-factoring could assist there, and that's something you should look at. But it's not a decision we can really make unless we see what the 'real' code is.
如果你需要传递20个参数,那么传递它们。可能这是必需的,因为你的功能太多而且重新分解可以帮助那些,这是你应该看的东西。但除非我们看到“真正的”代码是什么,否则我们无法做出决定。
#3
17
You can easily change the maximum allowed number of arguments in pylint. Just open your pylintrc file (generate it if you don't already have one) and change:
您可以轻松更改pylint中允许的最大参数数量。只需打开你的pylintrc文件(如果你还没有它就生成它)并更改:
max-args=5
to:
max-args = 6 # or any value that suits you
max-args = 6#或任何适合您的值
From pylint's manual
来自pylint的手册
Specifying all the options suitable for your setup and coding standards can be tedious, so it is possible to use a rc file to specify the default values. Pylint looks for /etc/pylintrc and ~/.pylintrc. The --generate-rcfile option will generate a commented configuration file according to the current configuration on standard output and exit. You can put other options before this one to use them in the configuration, or start with the default values and hand tune the configuration.
指定适合您的设置和编码标准的所有选项可能很繁琐,因此可以使用rc文件指定默认值。 Pylint查找/ etc / pylintrc和〜/ .pylintrc。 --generate-rcfile选项将根据标准输出和退出的当前配置生成注释配置文件。您可以在此之前添加其他选项以在配置中使用它们,或者从默认值开始并手动调整配置。
#4
12
You could try using Python's variable arguments feature:
您可以尝试使用Python的变量参数功能:
def myfunction(*args):
for x in args:
# Do stuff with specific argument here
#5
7
Perhaps you could turn some of the arguments into member variables. If you need that much state a class sounds like a good idea to me.
也许您可以将一些参数转换为成员变量。如果你需要那么多的状态,那么对我来说,一堂课听起来不错。
#6
5
Simplify or break apart the function so that it doesn't require nine arguments (or ignore pylint, but dodges like the ones you're proposing defeat the purpose of a lint tool).
简化或分解函数,使其不需要九个参数(或忽略pylint,但像你提议的那样闪避一个lint工具的目的)。
EDIT: if it's a temporary measure, disable the warning for the particular function in question using a comment as described here: http://lists.logilab.org/pipermail/python-projects/2006-April/000664.html
编辑:如果这是一个临时措施,请使用此处所述的评论禁用相关特定功能的警告:http://lists.logilab.org/pipermail/python-projects/2006-April/000664.html
Later, you can grep for all of the disabled warnings.
稍后,您可以查看所有已禁用的警告。
#7
3
Comment about paxdiablo's reply - as I do not have enough reputation to comment there directly :-/
评论paxdiablo的回复 - 由于我没有足够的声誉直接在那里发表评论: - /
I do not like referring to the number, the sybolic name is much more expressive and avoid having to add a comment that could become obsolete over time.
我不喜欢引用数字,sybolic名称更具表现力,并且避免添加可能随着时间的推移而过时的评论。
So I'd rather do:
所以我宁愿这样做:
#pylint: disable-msg=too-many-arguments
And I would also recommend to not leave it dangling there: it will stay active until the file ends or it is disabled, whichever comes first.
而且我还建议不要将它悬挂在那里:它将保持活动状态,直到文件结束或被禁用,以先到者为准。
So better doing:
做得更好:
#pylint: disable-msg=too-many-arguments
code_which_would_trigger_the_msg
#pylint: enable-msg=too-many-arguments
I would also recommend enabling/disabling one single warning/error per line.
我还建议每行启用/禁用一个警告/错误。
#8
0
Python has some nice functional programming tools that are likely to fit your needs well. Check out lambda functions and map. Also, you're using dicts when it seems like you'd be much better served with lists. For the simple example you provided, try this idiom. Note that map would be better and faster but may not fit your needs:
Python有一些很好的函数编程工具,可能很好地满足您的需求。查看lambda函数和map。此外,当你似乎更好地使用列表时,你正在使用dicts。对于您提供的简单示例,请尝试此习语。请注意,地图会更好更快,但可能不符合您的需求:
def mysum(d):
s = 0
for x in d:
s += x
return s
def mybigfunction():
d = (x1, x2, x3, x4, x5, x6, x7, x8, x9)
return mysum(d)
You mentioned having a lot of local variables, but frankly if you're dealing with lists (or tuples), you should use lists and factor out all those local variables in the long run.
你提到有很多局部变量,但坦率地说,如果你正在处理列表(或元组),你应该使用列表并从长远来看将所有这些局部变量分解出来。