传递对象引用的良好实践



我来自c++,我在用Python编程时努力获得一种安全感(例如,拼写错误会产生极其难以发现的错误,但这不是这里的重点)。在这里,我想了解如何通过坚持良好的实践来避免做可怕的事情。

下面的简单函数在c++中是完美的,但在Python中创建了一个我只能称之为怪物的东西。

def fun(x): 
x += 1
x = x + 1
return x

当我调用

var1 = 1;
print(fun(var1), var1)
var2 = np.array([1]);
print(fun(var2), var2)

3 1
[3] [2]

除了缺乏同质行为(这已经很可怕了),第二种情况尤其可怕。外部变量只被一些指令修改!

我知道它发生的详细原因。所以这不是我的问题。关键是,当构建一个复杂的程序时,我不想对所有这些依赖于上下文和高度隐式的技术细节格外小心。

必须有一些好的实践,我可以严格遵守,以防止我无意中产生上面的代码。我可以想到一些方法,但它们似乎使代码过于复杂,使c++看起来像一种更高级的语言。

我应该遵循什么好的实践来避免这个怪物?

谢谢!

[EDIT]澄清一下:我所纠结的是Python在创建临时时既依赖于类型又依赖于上下文。再说一遍,我知道规则。然而,在c++中,选择是由程序员完成的,并且在整个函数中都是清晰的,而在Python中则不是这样。Python要求程序员了解对实参所做操作的相当多的技术细节,以便弄清楚Python在这一点上是在处理临时还是原始。

请注意,我构造了一个函数,它既返回一个值,又有一个副作用,只是为了表明我的观点。

问题的关键在于程序员可能希望编写的函数只具有副作用(没有返回语句),并且在函数中途Python决定构建一个临时函数,因此不会应用一些副作用。另一方面,程序员可能不希望产生副作用,反而会产生一些副作用(而且很难预测)。

在c++中被简单而清晰地处理。在Python中,这是相当技术性的,需要知道什么触发了临时变量的生成,什么不触发。由于我需要向我的学生解释这一点,我想给他们一个简单的规则,以防止他们落入这些陷阱。

避免这些陷阱的良好实践:

  • 修改输入的函数不应该返回任何东西(例如list.sort)
  • 不修改输入的函数应该返回修改后的值(例如sorted)

你的fun两者都做,这违背了大多数标准库代码和流行的第三方Python库遵循的约定。打破这个"不成文的规则"是造成那里特别可怕的结果的原因。

一般来说,函数最好保持"纯净"。在可能的情况下。纯函数和无状态函数更容易推理,也更容易测试。

"safety&quot感;使用Python进行编程的关键在于拥有一个好的测试套件。作为一种解释型动态编程语言,Python中的几乎所有内容都发生在运行时。在编译时几乎没有什么可以保护您的—几乎只有语法错误会被发现。这对于灵活性来说是很好的,例如,几乎任何东西都可以在运行时进行monkeypatched。能力越大责任越大对于Python项目来说,测试代码是库代码的两倍是很正常的。

我想到的一个好的做法是命令-查询分离:

一个函数或方法应该只要么计算和返回一些东西,做一些事情,至少当涉及到外部可观察的行为时。

很少有例外是可以接受的(例如Stack数据结构的pop方法:它返回一些东西,做一些事情),但这些往往是在它如此习惯的地方,你不会期望它有任何其他方式。

当一个函数对它的输入值做一些事情时,这应该是该函数的唯一目的。这样,就不会有令人讨厌的意外了。

现在对于一个"原语"类型和更复杂的类型,最容易进行防御性编码,并假设它是一个引用。

相关内容