Last Updated on
在Python中,类和实例都是对象(虽然实例是类创建的),有其自己的属性和方法,我们称之为类变量和实例变量,其实用类属性和实例属性来称呼它们也是可以的,但是变量这个词已经成为程序语言的习惯称谓。
类变量
是在类的所有实例之间共享的属性和方法(也就是说,它们不是单独分配给每个实例的)
实例变量
实例化之后,每个实例单独拥有的变量。
怎么理解呢,看下面示例:
class Test(object):
num_of_instance = 0
def __init__(self, name):
self.name = name
Test.num_of_instance += 1
print("before:", Test.num_of_instance)
t1 = Test('jack')
print("create t1 after:", Test.num_of_instance)
t2 = Test('lucy')
print("create t2 after:", Test.num_of_instance)
# 输出结果:
before: 0
create t1 after: 1
create t2 after: 2
可以看到,在类中直接定义的属性num_of_instance就是类属性,在__init__方法下定义的self.name就是实例属性,__init__方法是创建实例时的初始化方法,会将实例self作为第一个参数传入,所以在其中通过self即可定义实例属性。
而且由于Python是动态语言,可以在运行的时候,可以新定义或者修改,删除类属性和实例属性,比如:
class Test(object):
num_of_instance = 0
Test.new_attr = 'new_attr'
print(Test.new_attr)
Test.num_of_instance = 1
print(Test.num_of_instance)
del Test.num_of_instance
print(Test.num_of_instance)
# 输出结果:
new_attr
1
AttributeError: type object 'Test' has no attribute 'num_of_instance'
可以看到,在类定义好后,在运行中,同样可以新增,修改,删除类属性。
同样的,与类属性相同,实例属性,同样的除了在定义时指定,也可以在运行中进行新增,修改,删除等:
class Test(object):
def __init__(self, name, age):
self.name = name
self.age = age
amos = Test('Amos', 24)
amos.sex = 'male'
print(amos.sex)
amos.age = 25
print(amos.age)
del amos.sex
print(amos.sex)
# 输出结果:
male
25
AttributeError: 'Test' object has no attribute 'sex'
类变量和实例变量的引用
类变量的引用,也就是两部分,方法和属性,看下面示例:
class Test(object):
name = 'myname'
age = 20
def __init__(self, age):
self.age = age
def work(self):
print("{} {} is working".format(self.name, self.age))
@classmethod
def sleep(cls):
print("{} {} is sleeping".format(cls.name, cls.age))
amos = Test(24)
amos.work()
amos.sleep()
Test.work()
Test.sleep()
# 输出结果:
myname 24 is working
myname 20 is sleeping
myname 24 is working
myname 20 is sleeping
细心的朋友会发现,虽然定义的name是类属性,但是通过实例self.name却能够输出该类属性,这是因为实例对象可引用类属性以及实例属性,其引用遵循以下规则:
- 总是先到实例对象中查找属性,再到类属性中查找属性;
- 属性绑定语句总是为实例对象创建新属性,属性存在时,更新属性指向的对象。
所以在实例方法work()中,可以通过self.name获取到类属性,但是由于实例的self.age属性,在实例化时定义了,所以在使用self.age时,会使用实例属性而不是类属性。
我们还发现,实例amos是可以调用类方法的,默认会将创建此实例的类作为输入,使用的属性则是类属性,即使实例的age已经变化,但是依然类属性依然存在且可以使用。
至于类方法自然也可以直接使用类型进行调用,此方法与实例没有关系,不会使用到实例的属性和方法。通过类名调用实例方法时,则会需要传入self参数(即使需要传入实例),比如我将实例amos传入,才能正常调用,直接是无法调用的。
至于@classmethod定义类方法的相关问题,不了解的可以查我的另一篇博文:
《Python @staticmethod和@classmethod》
我们再来看一个例子:
class Test(object):
name = 'myname'
likes = ['travel']
amos = Test()
amos.name = amos.name.strip('my')
amos.likes.append('read')
print(amos.name)
print(Test.name)
print(amos.likes)
print(Test.likes)
# 输出结果:
name
myname
['travel', 'read']
['travel', 'read']
可以发现,我们只定义了类变量,因为实例可以引用类变量,我们直接通过实例修改类变量时,默认是会优先创建新的实例变量,但是如果如果存在类变量,且类变量是可变对象,则会直接修改类变量,不会创建新的实例变量,如果是不可变对象,则会创建新的实例变量。
如上,字符串变量为不可变对象,所以修改后,会创建新的实例变量。而列表是可变对象,所以可以直接修改类属性。
在python中,strings, tuples, 和numbers是不可更改的对象,而 list, dict, set 等则是可以修改的对象。