ref:
http://c.biancheng.net/view/2283.html
https://www.cnblogs.com/crazyrunning/p/6945183.html
https://cloud.tencent.com/developer/article/1527548
学过C++ 或java等强类型,编译型语言,再去学Python类设计时感觉非常不适应,因为Python是在太自由了,让人无所适从,来Python定义类变量、实例对象变量的定义方式、作用域、访问方法的不同吧。
Python变量的本质:被赋值
1 普通python变量(非类相关变量)很容易理解,在被赋值后即变量存在,可读可写
2 Python类的变量(类变量和实例对象变量)以某种方式在某地方被赋值,即存在,可被读写
Python类变量被赋值
(1)类的设计里,
class里def外,通过变量名能被赋值
def里通过类对象即类名字的点运算变量名可被赋值
(2)程序里
通过类对象(类名字)的点运算类名字也可被赋值
Python实例对象变量被赋值
(1)类的设计时
def里通过self点运算变量名能被赋值,不一定非在init里,其他已被调用的方法函数里也行
(2)程序里
通过实例对象的点运算变量名可被赋值
类变量和实例变量的区别在于:类变量是所有对象共有,其中一个对象将它值改变,其他对象得到的就是改变后的结果;而实例变量则属对象私有,某一个对象将其值改变,不影响其他对象
在类体中,根据变量定义的位置不同,以及定义的方式不同,类属性又可细分为以下 3 种类型:
类体中、所有函数之外:此范围定义的变量,称为类属性或类变量;
类体中,所以函数内部:以“self.变量名”的方式定义的变量,称为实例属性或实例变量;
类体中,所有函数内部:以“变量名=变量值”的方式定义的变量,称为局部变量。
类变量(类属性)
类变量指的是在类中,但在各个类方法外定义的变量。
类变量的特点是,在类的所有实例之间共享的变量,也就是说,类变量在所有实例化对象中是作为公用资源存在的,不是单独分配给每个实例的。类方法的调用方式有 2 种,既可以使用类名直接调用,也可以使用类的实例化对象调用。
注意,因为类变量为所有实例化对象共有,通过类名修改类变量的值,会影响所有的实例化对象。
python独特的动态语言的灵活性,还可以随时随地动态地为类和对象添加类变量。
实例变量(实例属性)
实例变量指的是在任意类方法内部,以“self.变量名”的方式定义的变量,其特点是实例化之后,每个实例单独拥有一份的变量,只作用于调用方法的对象。另外,实例变量只能通过对象名访问,无法通过类名访问。
通过类对象访问类变量(instance.__class__.value),通过类对象修改类变量的值 (instance.value)很可能是定义新的实例变量。
Python还支持为特定的实例对象添加实例变量,在类体外,运行的时候。这种场景新增的特定的实例变量只对该特定实例对象生效,其他实例对象不受影响。
局部变量
除了实例变量,类方法中还可以定义局部变量,注意局部变量只存在于def内,在def内定义的不带self的变量。和前者不同,局部变量直接以“变量名=值”的方式进行定义,例如:
class CLanguage :
# 下面定义了一个say实例方法
def count(self,money):
sale = 0.8*money
print("优惠后的价格为:",sale)
clang = CLanguage()
clang.count(100)
通常情况下,定义局部变量是为了所在类方法功能的实现。需要注意的一点是,局部变量只能用于所在函数中,函数执行完成后,局部变量也会被销毁。
class student():
age = 0 # 注意这里class内函数外的age/name:python语言跟其他语言不同的是,这里定义的是类变量,而其他语言定义的是实例变量
name = 'stu'
# age,name是类变量
def __init__(self,age,name):
self.age = age
self.name = name
# 访问实例变量(用self.age self.name)
student1 = student(18,'hello')
print(student1.name)
# 打印实例变量,输出hello
print(student.name)
# 打印类变量,输出stu
In [2]: class Student():
...: name = "张三"
...: age = 0
...: def __init__(self, name, age):
...: name = name
...: age = age
...:
In [3]: student = Student("李四", 19)
In [4]: student.name
Out[4]: '张三'
In [5]: Student.name
Out[5]: '张三'
是实例变量和类变量的区别问题。
我们接着看看对象student的变量有哪些:
In [6]: student.__dict__
Out[6]: {}
为空!
为什么为空呢?(暂时思考下)
我们再看看类Student的变量:
In [7]: Student.__dict__
Out[7]:
mappingproxy({'__dict__': <attribute '__dict__' of 'Student' objects>,
'__doc__': None,
'__init__': <function __main__.Student.__init__>,
'__module__': '__main__',
'__weakref__': <attribute '__weakref__' of 'Student' objects>,
'age': 0,
'name': '张三'})
这里我们要说一下实例变量的查找机制:
当在实例变量中查找不到某一变量时候,就会去类变量里查找,当再查找不到的时候就会在父类中查找,因此输出的name为张三。
之所以为空,是因为我们实例化对象的时候为指明实例变量的保存。现在修改一下实例化函数。
In [10]: class Student():
...: name = "张三"
...: age = 0
...: def __init__(self, name, age):
...: self.name = name
...: self.age = age
...:
...:
...:
In [11]: student = Student("李四", 19)
In [12]: student.name
Out[12]: '李四'
In [13]: Student.name
Out[13]: '张三'
我们传入的self只和实例化的对象有关和类无关,代表实例。
如何在实例方法中调用类变量呢?
In [14]: class Student():
...: name = "张三"
...: age = 0
...: sums = 123
...: def __init__(self, name, age):
...: self.name = name
...: self.age = age
...: print(Student.sums)
...: print(self.__class__.sums)
...:
...:
...:
...:
In [15]: student = Student('李四', 19)
123
123
class Person():
person = 'alive' #这是类变量
def __init__(self,name ,age):
self.name = name #这是实例变量
self.age = age #这是实例变量
self.gender = '男' #这是实例变量
#a = 456 # 这种写法a是def内的局部变量,不是类属性,不是实例属性,类外引用都会报错
place = ['地球','中国'] #这是类变量
# 若要在def内定义类属性,只有两种方式,如下:
>>> class Person():
person = 'alive' #这是类变量
def __init__(self,name ,age):
self.name = name #这是实例变量
self.age = age #这是实例变量
self.gender = '男' #这是实例变量
Person.a = 456 #不能写这里,会出错
self.__class__.b = 'hello'
>>> p1 = Person('wa', 18)
>>>
>>> p1.b
'hello'
>>> p1.a
456
>>> Person.a
456
>>> Person.b
'hello'
>>>
class aa:
w = 10
def __init__(self):
self.x = 11
self.y = 12
def add(self):
return self.x + self.y
a = aa()
print (a.add()) //输出23
# 下边两条指令各起何作用?结果是输出两个 20 么?还是两个13?还是?
aa.w = 20 # 修改类变量w值
a.w = 13 # 新增实例变量w
print (aa.w, a.w) //输出20 13,注意:原因是当类变量与实例变量同名时,实例方式访问时会优先访问实例变量
# 程序继续增加如下,怎样理解这t和q呢?他们是实例变量
a.t = 14
a.q = 15
print (a.t, a.q) //输出14 15
# 程序继续增加如下,怎样理解这m和n呢?他们是类变量
aa.m = 30
aa.n = 40
print (aa.m, aa.n) //输出30 40
# 好了再来个提升吧
# 程序继续增加,下列三个print语句都能正确执行么?为何?
b = aa()
print (b.x, b.y) //正确输出11 12
print (b.t, b.q) //出错,因为不是一个实例了
print (b.m, b.n) //正确输出30 40
实例变量(静态属性)【实例名.实例变量名】
1、实例变量是构造函数下的变量带self.变量
2、实例变量为每个实例本身独有,不可相互调用、新增、修改、删除,不可被类调用、新增、修改、删除
3、可以访问类变量
4、如果同时有类变量和实例变量,程序执行时,先访问实例变量,实例变量存在,会使用实例变量,实例变量不存在,会使用类变量
5、实例改类变量,不可修改,实际是在实例内存里创建了实例变量
6、新增、修改、删除实例变量n,不会影响到类变量n
7、a实例不能调用b实例的变量
8、实例变量可修改、新增、删除
类变量【类名.类变量名】
1、类变量在class内,但不在class的方法内,存在类的内存里
2、类变量是该类所有实例共享的变量,但是实例对象只能访问,不可修改,每个实例对象去访问同一个类变量都将得到相同结果【实例名.类变量名】
3、新增、修改、删除类变量n,不会影响到实例变量n
4、类无权访问实例名
5、类变量可修改、新增、删除