提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
1. 对于self的理解
self 参数的具体作用是什么呢?打个比方,如果把类比作造房子的图纸,那么类实例化后的对象是真正可以住的房子。根据一张图纸(类),我们可以设计出成千上万的房子(类对象),每个房子长相都是类似的(都有相同的类变量和类方法),但它们都有各自的主人,那么如何对它们进行区分呢?
当然是通过 self 参数,它就相当于每个房子的门钥匙,可以保证每个房子的主人仅能进入自己的房子(每个类对象只能调用自己的类变量和类方法)。
其实 Python 类方法中的 self 参数就相当于 C++ 中的 this 指针。
2. 类变量与实例变量
- 类体中、所有函数之外:此范围定义的变量,称为
类属性或类变量
,类似C++中类的static变量; - 类体中,所有函数内部:以“self.变量名”的方式定义的变量,称为
实例属性或实例变量
;
不仅如此,类方法也可细分为实例方法、静态方法和类方法,后续会做详细介绍。
类变量:
class CLanguage :
# 下面定义了2个类变量
name = "C语言中文网"
add = "http://c.biancheng.net"
# 下面定义了一个say实例方法
def say(self, content):
print(content)
name、add就是类变量。
类变量的特点是:所有类的实例化对象都同时共享类变量,也就是说,类变量在所有实例化对象中是作为公用资源存在的。类方法的调用方式有 2 种,既可以使用类名直接调用,也可以使用类的实例化对象调用(但不推荐使用实例化对象调用)。
#使用类名直接调用
print(CLanguage.name)
print(CLanguage.add)
#修改类变量的值
CLanguage.name = "Python教程"
CLanguage.add = "http://c.biancheng.net/python"
print(CLanguage.name)
print(CLanguage.add)
程序运行结果为:
C语言中文网
http://c.biancheng.net
Python教程
http://c.biancheng.net/python
可以看到,通过类名不仅可以调用类变量,也可以修改它的值。并且,具有“牵一发动全身”
的效果,也就是说,比如用这个类实例化了好多个对象,如果按照类名调用的方式修改了类变量,则所有的对象的类变量都会改变。
当然,也可以使用类对象来调用所属类中的类变量(此方式不推荐使用,原因见后续)。例如:
clang = CLanguage()
print(clang.name)
print(clang.add)
除了可以通过类名访问类变量之外,还可以动态地为类和对象添加类变量(只可以用类名添加类变量,而不能用对象添加类变量,原因见后续)。例如,在 CLanguage 类的基础上,添加以下代码:
clang = CLanguage()
CLanguage.catalog = 13
print(clang.catalog) #输出为13
实例变量:
实例变量指的是在任意类方法内部,以“self.变量名”的方式定义的变量
,其特点是只作用于调用方法的对象。另外,实例变量只能通过对象名访问,无法通过类名访问。
class CLanguage :
def __init__(self):
self.name = "C语言中文网"
self.add = "http://c.biancheng.net"
# 下面定义了一个say实例方法
def say(self):
self.catalog = 13
name、add 以及 catalog 都是实例变量。其中,由于 init() 函数在创建类对象时会自动调用,而 say() 方法需要类对象手动调用。因此,CLanguage 类的类对象都会包含 name 和 add 实例变量,而只有调用了 say() 方法的类对象,才包含 catalog 实例变量。
如:
clang = CLanguage()
print(clang.name)
print(clang.add)
#由于 clang 对象未调用 say() 方法,因此其没有 catalog 变量,下面这行代码会报错
#print(clang.catalog)
clang2 = CLanguage()
print(clang2.name)
print(clang2.add)
#只有调用 say(),才会拥有 catalog 实例变量
clang2.say()
print(clang2.catalog)
前面讲过,通过类对象可以访问类变量,但无法修改类变量的值。这是因为,通过类对象修改类变量的值,不是在给“类变量赋值”,而是定义新的实例变量。 例如,如下程序:
class CLanguage :
name_static = "Python"
add_static = "http"
def __init__(self):
self.name = "C语言中文网"
self.add = "http://c.biancheng.net"
# 下面定义了一个say实例方法
def say(self):
self.catalog = 13
clang = CLanguage()
#clang访问类变量
print(clang.name_static)
print(clang.add_static)
clang.name_static = "Python教程"
clang.add_static = "http://c.biancheng.net/python"
#clang实例变量的值
print(clang.name)
print(clang.add)
#类变量的值
print(CLanguage.name_static) #并没有任何改变
print(CLanguage.add_static) #并没有任何改变
#新增的实例变量的值
print(clang.name_static)
print(clang.add_static)
则输出为:
Python
http
C语言中文网
http://c.biancheng.net
Python
http
Python教程
http://c.biancheng.net/python
类中,实例变量和类变量可以同名,但这种情况下使用类对象将无法调用类变量,它会首选实例变量,这也是不推荐“类变量使用对象名调用”的原因。
另外,和类变量不同,通过某个对象修改实例变量的值,不会影响类的其它实例化对象,更不会影响同名的类变量。
- 可以动态的为特定的对象添加实例变量。如clang.money = 30。
3. 类方法、静态方法与实例方法
和类属性一样,类方法也可以进行更细致的划分,具体可分为类方法、实例方法和静态方法。
区分这 3 种类方法是非常简单的,即采用 @classmethod
修饰的方法为类方法;采用 @staticmethod
修饰的方法为静态方法;不用任何修改的方法为实例方法。
实例化方法
实例方法最大的特点就是,它最少也要包含一个 self 参数,用于绑定调用此方法的实例对象(Python 会自动完成绑定)。实例方法通常会用类对象直接调用,例如:
clang = CLanguage()
clang.say()
python也支持仅用类名来调用实例方法,但此方式需要手动给 self 参数传值。如:
#类名调用实例方法,需手动给 self 参数传值
clang = CLanguage()
CLanguage.say(clang)
类方法
Python 类方法和实例方法相似,它最少也要包含一个参数,只不过类方法中通常将其命名为 cls
, Python 会自动将类本身绑定给 cls 参数(注意,绑定的不是类对象),类方法中只能访问到类变量,访问不到实例变量。
(有点像c++中的static函数)。
和实例方法最大的不同在于,类方法需要使用@classmethod
修饰符进行修饰,例如:
class CLanguage:
name = "python"
#类构造方法,也属于实例方法
def __init__(self):
self.name = "C语言中文网"
self.add = "http://c.biancheng.net"
#下面定义了一个类方法
@classmethod
def info(cls):
print("正在调用类方法",cls)
print("类变量name = ",cls.name)
注意,如果没有 @classmethod,则 Python 解释器会将 info() 方法认定为实例方法,而不是类方法。
类方法推荐使用类名直接调用,当然也可以使用实例对象来调用(不推荐)。例如,在上面 CLanguage 类的基础上,在该类外部添加如下代码:
#使用类名直接调用类方法
CLanguage.info()
#使用类对象调用类方法
clang = CLanguage()
clang.info()
输出为:
正在调用类方法 <class '__main__.CLanguage'>
类变量name = python
正在调用类方法 <class '__main__.CLanguage'>
类变量name = python
一个关于类方法的具体实例:
class ColorTest(object):
color = "color"
@classmethod
def value(self):
return self.color
class Red(ColorTest):
color = "red"
class Green(ColorTest):
color = "green"
g = Green()
print(g.value())
print(Green.value())
输出为:
green
green
静态方法
静态方法没有类似 self、cls 这样的特殊参数,因此 Python 解释器不会对它包含的参数做任何类或对象的绑定。也正因为如此,类的静态方法中无法调用任何类属性和类方法。
(感觉这种函数没啥意义)
静态方法需要使用@staticmethod
修饰,例如:
class CLanguage:
@staticmethod
def info(name,add):
print(name,add)
静态方法的调用,既可以使用类名,也可以使用类对象,例如:
#使用类名直接调用静态方法
CLanguage.info("C语言中文网","http://c.biancheng.net")
#使用类对象调用静态方法
clang = CLanguage()
clang.info("Python教程","http://c.biancheng.net/python")
输出为:
C语言中文网 http://c.biancheng.net
Python教程 http://c.biancheng.net/python
一个关于静态方法的具体实例:
import time
class TimeTest(object):
def __init__(self,hour,minute,second):
self.hour = hour
self.minute = minute
self.second = second
@staticmethod
def showTime():
return time.strftime("%H:%M:%S", time.localtime())
print TimeTest.showTime()
t = TimeTest(2,10,10)
nowTime = t.showTime()
print nowTime
如上,使用静态函数,既可以将获得时间的函数功能与实例解绑,我想获得当前时间的字符串时,并不一定需要实例化对象,此时更像是一种名称空间(类似c++中的namespace)。
我们可以在类外面写一个简单的方法来做这些,但是这样做就扩散了类代码的关系到类定义的外面,这样写就会导致以后代码维护的困难。
在实际编程中,几乎不会用到类方法和静态方法,因为我们完全可以使用函数代替它们实现想要的功能,但在一些特殊的场景中(例如工厂模式中),使用类方法和静态方法也是很不错的选择。