今天学习了一下 python 的类属性和实例属性,感觉进入了一个新天地。
# 500px 用户
class User(object):
id = 0
name = ''
def __init__(self, id, name):
self.id = id
self.name = name
这是我在做 500px 爬虫时定义的一个类。作为一个 java 程序猿,当时我脑海里是这样的
public class User {
private int id;
private String name;
public User(int id, String name) {
this.id = id;
this.name = name;
}
public int getId() {
return id;
}
public String getName() {
return name;
}
public void setId(int id) {
this.id = id;
}
public void setName(String name) {
this.name = name;
}
}
但是经过今天的学习以后,我发觉有哪里不对。恐怕我的理解有个很大的误区。如果我仍然想要用 python 来映射我脑海里的 java 代码,那么我应该这样写
# 500px 用户
class User(object):
def __init__(self, id, name):
self.id = id
self.name = name
即,不声明类属性。实际上,java 里并不存在能对应 python 类属性的东东(即使是类的静态属性依然不完全对应),如果一定要用 java 来实现第一段 python 代码的效果,那我只能这么办
public class User {
public static id = 0;
public static name = "";
private int _id;
private String _name;
public User(int id, String name) {
this._id = id;
this._name = name;
}
public int getId() {
return _id;
}
public String getName() {
return _name;
}
public void setId(int id) {
this._id = id;
}
public void setName(String name) {
this._name = name;
}
}
这一段 java 代码没有语法错误,也能表达出第一段 python 代码的思路,但仅从 java 的角度来评价,这个代码很挫。原因就是静态变量 id
, name
和 get/setId()
, get/setName()
之间的 同名 关系令人迷惑。
也就是说,我根本不应该在 python 代码里声明类属性 id
, name
。我的本意是想要表示 User
类有 2 个属性,整数类型的 id
和 字符串类型的 name
,但这完全是 java 的思路,到了 python 这里,就产生了副作用。
以一个 java 程序猿的语言来解释 python 的类属性,那就是 java 类的 静态变量,但 python 类的实例属性可以和类属性同名并且实例属性优先,这个在 java 里很难解释,勉强要解释的话,就是我上面那段 java 代码。
那么,如果我不在 python 类里声明类属性,就如同我在第二段 python 代码里写的那样,有什么问题呢?
问题就在于没有事先声明这个类有哪些属性,令人不安,这会导致其他人或者多年以后的自己,很难看懂这个类。或者,__init__
里的赋值就是一种变相的属性声明?这样想来,多少有点安慰。
也许这证明我还没有真正理解 python 吧。
ps
类属性和实例属性同名并且实例属性优先,这是个大坑,为了避免掉到这个坑里,最好的做法是
- 不声明类属性
- 如果一定要声明类属性,那就重写
__init__
方法,给类属性同名的实例属性赋初始值
我觉得最佳实践是,不声明类属性,同时重写 __init__
方法