任何修补Python足够长的人都被以下问题咬伤(或撕成碎片):
def foo(a=[]):
a.append(5)
return a
Run Code Online (Sandbox Code Playgroud)
Python新手希望这个函数总能返回一个只包含一个元素的列表:[5].结果却非常不同,而且非常惊人(对于新手来说):
>>> foo()
[5]
>>> foo()
[5, 5]
>>> foo()
[5, 5, 5]
>>> foo()
[5, 5, 5, 5]
>>> foo()
Run Code Online (Sandbox Code Playgroud)
我的一位经理曾经第一次遇到这个功能,并称其为该语言的"戏剧性设计缺陷".我回答说这个行为有一个潜在的解释,如果你不理解内部,那确实非常令人费解和意想不到.但是,我无法回答(对自己)以下问题:在函数定义中绑定默认参数的原因是什么,而不是在函数执行时?我怀疑经验丰富的行为有实际用途(谁真的在C中使用静态变量,没有繁殖错误?)
编辑:
巴泽克提出了一个有趣的例子.再加上你的大部分评论和特别是Utaal,我进一步阐述了:
>>> def a():
... print("a executed")
... return []
...
>>>
>>> def b(x=a()):
... x.append(5)
... print(x)
...
a executed
>>> b()
[5]
>>> b()
[5, 5]
Run Code Online (Sandbox Code Playgroud)
对我而言,似乎设计决策是相对于放置参数范围的位置:在函数内部还是"与它一起"?
在函数内部进行绑定意味着在调用函数时x有效地绑定到指定的默认值,而不是定义,这会产生一个深层次的缺陷:def在某种意义上,该行将是"混合"的(部分绑定)函数对象)将在定义时发生,并在函数调用时发生部分(默认参数的赋值).
实际行为更加一致:执行该行时,该行的所有内容都会得到评估,这意味着在函数定义中.
python language-design least-astonishment default-parameters
我知道Python不支持方法重载,但我遇到了一个问题,我似乎无法以一种漂亮的Pythonic方式解决这个问题.
我正在制作一个角色需要射击各种子弹的游戏,但是如何编写不同的功能来制作这些子弹呢?例如,假设我有一个函数可以创建一个以给定速度从A点到B点行进的子弹.我会写一个这样的函数:
def add_bullet(sprite, start, headto, speed):
... Code ...
Run Code Online (Sandbox Code Playgroud)
但我想写其他功能来创建子弹,如:
def add_bullet(sprite, start, direction, speed):
def add_bullet(sprite, start, headto, spead, acceleration):
def add_bullet(sprite, script): # For bullets that are controlled by a script
def add_bullet(sprite, curve, speed): # for bullets with curved paths
... And so on ...
Run Code Online (Sandbox Code Playgroud)
等等有很多变化.有没有更好的方法来做到这一点,而不使用这么多的关键字参数导致它快速变得有点难看.重命名每个功能,因为你要么是非常糟糕过add_bullet1,add_bullet2或add_bullet_with_really_long_name.
要解决一些问题:
不,我无法创建Bullet类层次结构,因为它太慢了.管理项目符号的实际代码在C中,我的函数是围绕C API的包装器.
我知道关键字参数,但检查各种参数组合变得烦人,但默认参数有助于分配 acceleration=0
是否有可能在Python中重载函数?在C#中,我会做类似的事情
void myfunction (int first, string second)
{
//some code
}
void myfunction (int first, string second , float third)
{
//some different code
}
Run Code Online (Sandbox Code Playgroud)
然后当我调用函数时,它会根据参数的数量区分两者.是否有可能在Python中做类似的事情?
如上所述:
Python中没有函数重载.
据我所知,这也是一个很大的障碍,因为它也是一种OO语言.最初我发现无法区分参数类型很困难,但Python的动态特性使其变得容易(例如,列表,元组,字符串非常相似).
然而,计算传递的参数数量然后完成工作就像是一种矫枉过正.
在编写单元测试时,我有时剪切并粘贴测试,不记得更改方法名称.这导致覆盖先前的测试,有效地隐藏它并阻止它运行.例如;
class WidgetTestCase(unittest.TestCase):
def test_foo_should_do_some_behavior(self):
self.assertEquals(42, self.widget.foo())
def test_foo_should_do_some_behavior(self):
self.widget.bar()
self.assertEquals(314, self.widget.foo())
Run Code Online (Sandbox Code Playgroud)
在这种情况下,只会调用后一个测试.是否有一种以编程方式捕获此类错误的方法,而不是直接解析原始源代码?
我正在从Java背景学习Python(3.x).
我有一个python程序,我在其中创建一个personObject并将其添加到列表中.
p = Person("John")
list.addPerson(p)
Run Code Online (Sandbox Code Playgroud)
但是为了灵活性,我还希望能够直接在addPerson方法中声明它,如下所示:
list.addPerson("John")
Run Code Online (Sandbox Code Playgroud)
addPerson方法将能够区分我是否发送Person-object或String.
在Java中,我将创建两个单独的方法,如下所示:
void addPerson(Person p) {
//Add person to list
}
void addPerson(String personName) {
//Create Person object
//Add person to list
}
Run Code Online (Sandbox Code Playgroud)
我无法在Python中找到如何做到这一点.我知道一个type()函数,我可以用它来检查参数是String还是Object.然而,这对我来说似乎很混乱.还有另一种方法吗?
编辑:
我想替代解决方法将是这样的(python):
def addPerson(self, person):
//check if person is string
//Create person object
//Check that person is a Person instance
//Do nothing
//Add person to list
Run Code Online (Sandbox Code Playgroud)
但与Java中的重载解决方案相比,它看起来很混乱.
>>> from typing import overload
>>> @overload
... def hello(s: int):
... return "Got an integer!"
>>> def hello(s: str):
... return "Got a string"
Run Code Online (Sandbox Code Playgroud)
为什么调用者hello(1)使用字符串参数调用函数?理想情况下,@overload操作员应该处理它,对吗?
我想在Python中实现函数重载。我知道默认情况下Python不支持重载。这就是我要问的问题。
我有以下代码:
def parse():
results = doSomething()
return results
Run Code Online (Sandbox Code Playgroud)
x = namedtuple('x',"a b c")
def parse(query: str, data: list[x]):
results = doSomethingElse(query, data)
return results
Run Code Online (Sandbox Code Playgroud)
我能想到的唯一解决方案是检查参数:
def parse(query: str, data: list[x]):
if query is None and data is None:
results = doSomething()
return results
else:
results = doSomethingElse(query, data)
return results
Run Code Online (Sandbox Code Playgroud)
是否可以像 Java 一样在 Python 中进行函数重载(即没有分支)?
有没有使用装饰器或某些库的明确方法?
嗨,我正在学习计算机科学,我要求我必须在python中编写两个具有相同名称的函数.我该怎么办?
class QueueCards:
def __init__(self):
self.cards = []
def Add(self, other):
self.cards.insert(0, other)
def Add(self, listCards, numCards):
for i in numCards:
card = listCards.GetCard(i)
self.Add(card)
Run Code Online (Sandbox Code Playgroud)
Count()是队列的大小.谢谢.
这个问题不是另一个重载问题的重复,因为我试图self.args在函数中引用.在那个问题中给出的答案并不满足,因为如果我实现了这个答案,我将得到一个错误.但是,在其他语言中,这可以通过方法重载轻松完成......所以这个问题与另一个问题高度相关.
所以我有一个方法,
class learner():
def train(a ton of arguments):
self.argA = argA,
etc.
Run Code Online (Sandbox Code Playgroud)
我想用一个值调用train,并让它使用所有的self调用来填充其他参数......但它是python似乎不支持的循环引用.理想情况下,我会写:
class learner():
def train(self, position, data = self.data, alpha = self.alpha, beta = etc):
...do a bunch of stuff
def roll_forward(self,position):
self.alpha += 1
self.beta += 1
self.train(position)
Run Code Online (Sandbox Code Playgroud)
我该怎么做?在其他语言中,我可以定义第二个train访问内部变量的函数...
目前,我有一个janky hack我这样做:
class learner():
def train(...):
....
def train_as_is(self,position):
self.train(position,self.alpha,self.beta, etc.)
Run Code Online (Sandbox Code Playgroud)
但这是一个相当大的课程,而且很多这样的功能都将我的代码变成意大利面...
我无法理解干净的方法来做到这一点。我想要一个名为set_delay()带有各种参数的函数。我可以设置 3 种不同的“延迟类型”:恒定、均匀和正常。这是我目前拥有的:
def set_delay_constant(delay):
continue
def set_delay_uniform(min_delay, max_delay):
continue
def set_delay_normal(mean, std_dev, timeout):
continue
Run Code Online (Sandbox Code Playgroud)
我遇到的问题是每个函数中大约 80% 的代码是重复的。我见过的想法是:
def set_delay(delay_type, delay=None, min_delay=None, max_delay=None, mean=None, std_dev=None, timeout=None):
continue
Run Code Online (Sandbox Code Playgroud)
但是当我需要用更多的延迟类型来扩展它时,我可以看到它变得非常长并且难以阅读。最“Pythonic”的方法是什么?谢谢你!