标签: least-astonishment

"最小的惊讶"和可变的默认论证

任何修补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

2458
推荐指数
31
解决办法
14万
查看次数

为什么负面的id或零被视为不良做法?

在数据库表中插入主键时,为什么将负id或零视为不良做法?

我认为它在某些情况下可能有用,但是人们说它不推荐,尽管它们从不说/知道为什么.

所以,我想知道是否存在,根据定义,是否有一些限制,或者它是否应该没有任何问题,或者它是否仅仅是一个约定,如果真的存在一些限制,为什么不阻止该功能?

database database-design primary-key identifier least-astonishment

22
推荐指数
2
解决办法
1万
查看次数

Python中的"布尔"操作(即:和/或运算符)

此方法搜索第一组单词字符(即:)[a-zA-Z0-9_],返回第一个匹配的组或None出现故障.

def test(str):
    m = re.search(r'(\w+)', str)
    if m:
        return m.group(1)
    return None
Run Code Online (Sandbox Code Playgroud)

相同的功能可以改写为:

def test2(str):
    m = re.search(r'(\w+)', str)
    return m and m.group(1)
Run Code Online (Sandbox Code Playgroud)

这工作原理相同,并且是记录在案的行为; 作为此页中明确指出:

表达式x and y首先评估x; 如果x为false,则返回其值; 否则,y将评估并返回结果值.

但是,作为一个布尔运算符(它甚至在手册上都这么说),我期望and返回一个布尔值.结果,当我发现(如何)这有效时,我感到惊讶.

有什么其他用例,和/或这种相当不直观的实现的理由是什么?

python boolean-expression least-astonishment

11
推荐指数
2
解决办法
5967
查看次数

不应该使用FieldInfo.SetValue将ValueType设置为null失败?

(与PropertyInfo SetValue和nulls相关)

如果我有public class Thing { public int X; },a Thing o和a FieldInfo fi指向该X字段,为什么打电话合法fi.SetValue(o, null)?运行时将字段X设置为零,即default(int)不是抱怨a ValueType不能设置为null.

有没有人知道这种行为背后的设计选择,至少从C#中违背了我最不惊讶的原则?

.net clr least-astonishment

6
推荐指数
1
解决办法
543
查看次数

在Python属性中允许使用特殊字符的原因

我有些偶然地发现,您可以使用设置对象的“非法”属性setattr。非法是指__getattr__具有传统.操作员引用的接口无法检索的名称属性。它们只能通过getattr方法检索。

在我看来,这似乎非常令人惊讶,我想知道是否有这个原因,或者只是被忽略了,等等。由于存在用于检索属性的运算符和setattribute接口的标准实现,所以我希望它仅允许实际上可以正常检索的属性名称。而且,如果出于某些奇怪的原因想要具有无效名称的属性,则必须为它们实现自己的接口。

我一个人会为这种行为感到惊讶吗?

class Foo:
    "stores attrs"

foo = Foo()
setattr(foo, "bar.baz", "this can't be reached")
dir(foo)
Run Code Online (Sandbox Code Playgroud)

这将返回既奇怪又有点误导的东西: [...'__weakref__', 'bar.baz']

而且,如果我想以“标准”方式访问foo.bar.baz,则不能。无法检索它是很有意义的,但是设置它的能力令人惊讶。

foo.bar.baz
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'Foo' object has no attribute 'bar'
Run Code Online (Sandbox Code Playgroud)

是否只是假设,如果必须使用setattr来设置变量,您将通过引用它getattr?因为在运行时,这可能并不总是正确的,尤其是使用Python的交互式解释器,反射等时。默认情况下允许这样做仍然很奇怪。

编辑:我希望看到的作为setattr的默认实现的一个(非常粗糙的)示例:

import re

class Safe:
    "stores attrs"

    def __setattr__(self, attr, value):
        if not re.match(r"^\w[\w\d\-]+$", attr):
            raise AttributeError("Invalid characters in attribute name")
        else:
            super().__setattr__(attr, …
Run Code Online (Sandbox Code Playgroud)

python least-astonishment

6
推荐指数
1
解决办法
736
查看次数

多层架构中的默认函数值

想知道在多层应用程序结构中设置默认值的最佳方法。具体来说,如果某个工作流程需要一组嵌套的函数调用,那么默认值是在所有函数上指定,还是仅在顶层函数上指定并向下传递?或者完全是其他模式?

例如,考虑一个具有 3 层的 Web 应用程序:

  • 资源层处理HTTP请求和响应,从客户端获取HTTP参数
  • 业务层执行确定需要什么信息所需的业务逻辑
  • 数据层访问数据库并返回所请求的数据。

假设客户端想要获取一个对象——对于这个例子,假设它是一个Place对象。地点对象有type——城市、州、城镇、县等。

资源函数可能如下所示(使用 Django HTTP 对象):

def resource_get_place(request, type=None):
    """ Gets a place of the specified type, or a place of any type if
     type is None """

   place = business_get_place(type=type)

   return HttpResponse(request, {
       "place" : place
   }
Run Code Online (Sandbox Code Playgroud)

那么另外两个可能是:

def business_get_place(type):
    # Trivial in this case, used for consistency
    return data_get_place(type)

def data_get_place(type):
    # Get a place of specified type from the cache, or db,
    # …
Run Code Online (Sandbox Code Playgroud)

architecture default dry three-tier least-astonishment

5
推荐指数
1
解决办法
265
查看次数