类属性:
1、基本特点:
通常情况下通过类方法来修改类属性,这样避免很多麻烦。当然,类名和对象也可以调用修改,区别是类名修改的是唯一备份,所有的对象都要跟着改变;对象也可以在类外调用类属性进行修改,会产生“屏蔽”,也就会给当前对象创建新的实例属性,进而通过简单的对象访问,访问不到类属型,只会返回和修改实例属性,相当于类属型暂时对此对象“屏蔽”了。
class A(object):
B = 4 # 类属性B
a = A()
b = A()
a.B = 6 # 通过对象a修改类属性,其他对象的类属性因被屏蔽不会发生修改
print(a.B)
print(b.B) # 运行结果 6 4
print(a.__class__.B) # 结果是6
# a的类属性并没有被修改只是,被新创建的a实例属性屏蔽了
A.B = 6 # 通过类名修改类属性,直接修改了唯一备份,所以都会被修改
print(a.B)
print(b.B) # 运行结果 6 6
2、单例模式
在单例模式下的类属性,所有的对象被赋予了同一个内存地址。此时类属性被cls使用,而cls就等同于类名的调用,所以效果同上。联想到类方法传参也是cls,所以也是相同效果。
class A(object):
object_catch = None # 定义类属性object_catch用于捕获对象
def __new__(cls, num):
if cls.object_catch is None:
cls.object_catch = super().__new__(cls)
return cls.object_catch
def __init__(self, num):
self.num = num
# print("%s" % num)
a = A(1)
print(a.num)
A.object_catch = None # 通过类名给类属性赋值
b = A(2)
print(a.num, b.num) # 执行结果 1 2说明a,b是两个独立对象
a = A(1)
print(a.num)
a.object_catch = None # 通过类对象给类属性赋值,发生屏蔽
b = A(2)
print(a.num, b.num) # 执行结果 2 2说明a,b是不是独立对象
3、self使用类属性
单例模式下,如果我们的类属性通过self使用(如实例方法)的话,首先这是很不规范的,正是由于这种不规范操作导致了特殊情况
class A(object):
object_catch = None
def __new__(cls, mem):
if cls.object_catch is None:
cls.object_catch = super().__new__(cls)
return cls.object_catch
is_First = True
def __init__(self, mem):
# print(A.is_First) # 测试A.is_First的值
# print(self.is_First) # 测试self.is_First的值
if self.is_First is True:
self.name = mem
print("%s"%mem)
self.is_First = False
a = A(23)
a.is_First = True # 如果A.is_First = True 则b不会被实例化 执行结果:23
b = A(1) # 执行结果 23 1
此时由于在魔法方法中使用self调用了类属性is_First,不规范的使用导致只能通过对象调用is_First才能使得被修改的is_First发挥作用。
小结:
类属性的可以被类方法,实例方法,类名,对象调用。但是实例方法调用属于不规范的情况,需要特别注意,对象调用会出现屏蔽现象。 由此,类方法的意义也就显现出来了,他能够没有异议的对共有类属性修改而不出现屏蔽等现象。
从地址层面理解类属性:类A实例化对象a,b,对于类属性,实例属性,类方法,实例方法而言,A跟对象a,b的存储地址是相同的。对于类属性,我们使用类名或者类方法修改值,存储他们的存储地址是不会发生改变的(总是类中类属性的地址)。如果通过对象名修改类属性,则会发生“屏蔽”,导致对象的类属性地址发生改变,也就无法通过类改变类属性的值改变对象类属性的值,因为他们的地址已经不相同。