我来自 C++,在使用 Python 编程时,我很难获得安全感(例如拼写错误可能会导致极难发现错误,但这不是重点)。在这里,我想了解如何通过坚持良好的做法来避免做可怕的事情。
下面的简单函数在 C++ 中完全没问题,但在 Python 中却创建了我只能称之为怪物的东西。
def fun(x):
x += 1
x = x + 1
return x
Run Code Online (Sandbox Code Playgroud)
当我呼唤它时
var1 = 1;
print(fun(var1), var1)
var2 = np.array([1]);
print(fun(var2), var2)
Run Code Online (Sandbox Code Playgroud)
我明白了
3 1
[3] [2]
Run Code Online (Sandbox Code Playgroud)
除了缺乏同质行为(这已经很糟糕)之外,第二种情况尤其可怕。外部变量仅被部分指令修改!
我详细地知道为什么会发生这种情况。所以这不是我的问题。关键是,在构建复杂的程序时,我不想对所有这些依赖于上下文且高度隐含的技术细节格外小心。
必须有一些我可以严格遵守的良好实践,以防止我无意中生成上述代码。我可以想办法,但它们似乎使代码过于复杂,使 C++ 看起来像是一种更高级的语言。
我应该遵循什么好的做法来避免这种可怕的情况?
谢谢!
[编辑]一些澄清:我所困扰的是Python在创建临时变量时做出了依赖于类型和上下文的选择。再说一次,我知道规则。然而,在 C++ 中,选择是由程序员完成的,并且在整个函数中都是清晰的,而在 Python 中则不是这样。Python 要求程序员了解对参数执行的操作的一些技术细节,以便弄清楚此时 Python 是在处理临时变量还是原始变量。
请注意,我构造了一个函数,它既返回一个值,又具有副作用,只是为了表明我的观点。
要点是,程序员可能希望编写该函数以产生副作用(没有 return 语句),并且在该函数的中途,Python 决定构建一个临时函数,因此不会应用某些副作用。另一方面,程序员可能不想要副作用,而是会得到一些副作用(而且很难预测)。
在 C++ 中,上述内容的处理简单明了。在 Python 中,这是相当技术性的,需要知道什么触发临时变量的生成,什么不触发临时变量的生成。当我需要向我的学生解释这一点时,我想给他们一个简单的规则,以防止他们陷入这些陷阱。
我试图将一个函数声明为具有受保护成员的类模板的友元。下面是一个最小的例子。
template<int N> class myClass{
public:
friend void f(const myClass& c);
protected:
int a;
};
Run Code Online (Sandbox Code Playgroud)
如果我现在将函数定义为
template<int N> void f(const myClass<N>& c){
std::cout << c.a;
};
Run Code Online (Sandbox Code Playgroud)
然后就可以了。
但是如果我使用模板专业化
template<int N> void f(const myClass<N>& c);
template<> void f<1>(const myClass<1>& c){
std::cout << c.a;
};
Run Code Online (Sandbox Code Playgroud)
它不再承认f自己是朋友,并抱怨说自己a是受保护的成员。
为什么会发生这种情况?我究竟做错了什么?