python 基础知识 | 类属性和实例属性

类属性和实例属性

python中类和实例都维护一个__dict__字典,用来存放方法和属性,可以通过dir()函数查看,下面直接通过代码去说明类属性和实例属性的区别。

class Student():
	# 类属性
    num = '类属性'

    def __init__(self,name):
    	# 实例属性
        self.name = name
   	
   	def method1(self):
        print(self.name)

    @classmethod
    def method(cls):
        cls.age = '类属性'

上面定义了一个类Student

类属性

>>> # 1.查看类属性
>>> dir(Student)
[..., 'method', 'method1', 'num']

为了直观,默认的方法和属性就省略了。可以看到Student类维护的__dict__字典中,有类中定义的method和method1方法,还有类属性num。实例属性name不在字典中,实例属性由实例自身维护,也意味着无法通过类名去调用实例属性。此外,虽然实例方法在字典中,但是用类名访问会报错,如下:

>>> Student.method1()
Traceback (most recent call last):
  File "<interactive input>", line 1, in <module>
TypeError: method1() missing 1 required positional argument: 'self'

该报错说明,该方法需要一个self参数,即对象的实例,创建两个实例,如下:

>>> s1,s2=Student('u1'),Student('u2')

然后通过实例调用实例方法method1(),如下:

>>> # 解决上面类型调用出错的问题,不建议这样用
>>> Student.method1(s1)
u1
>>> # 用实例调用,不需要传递参数,解释器会自动将实例进行传递
>>> s1.method1()
u1

实例属性

>>> # 2.查看实例属性
>>> dir(s1)
[..., 'method', 'method1', 'name', 'num']
>>> dir(s2)
[..., 'method', 'method1', 'name', 'num']

为了直观,默认的方法和属性也省略了。可以看到实例s1和实例s2中维护的__dict__字典中,类方法和类属性都存在,即可以通过实例访问类方法和类属性

>>> # 证明两个实例指向的内存不一致
>>> s1 is s2
False
>>> # 证明两个实例指向的类属性的内存一致
>>> s1.num is s2.num
True
>>> # 证明两个实例指向的实例属性的内存不一致
>>> s1.name is s2.name
False

如上,实例s1和s2各自维护了自己的__dict__字典。

自定义类属性和实例属性

前面已经看到,类和实例都维护着一个__dict__列表,那么像操作列表一样,在类外能不能自定义类属性和方法属性?
答案是可以的,如下:

>>> Student.num2='类属性'
>>> dir(Student)
[..., 'method', 'method1', 'num', 'num2']
>>> dir(s1)
[..., 'method', 'method1', 'name', 'num', 'num2']
>>> dir(s2)
[..., 'method', 'method1', 'name', 'num', 'num2']
>>> # 证明实例间的属性互不影响
>>> s1.name2='实例属性'
>>> dir(s1)
[..., 'method', 'method1', 'name', 'name2', 'num', 'num2']
>>> dir(s2)
[..., 'method', 'method1', 'name', 'num', 'num2']

当我们定义了一个class,创建了一个class的实例后,可以给该类和实例绑定任何属性和方法,这就是动态语言的灵活性,但是这样使得代码的维护性降低了,不建议这样写,为了达到限制的目的,Python允许在定义class的时候,定义一个特殊的__slots__变量,来限制该class实例能添加的属性,如下:

>>> class Student():
...    __slots__=('name',)
...    num = '类属性'
...    def __init__(self,name):
...        self.name = name
...    @classmethod
...    def method(cls):
...        cls.age = '类属性'
...    def method1(self):
...    	   print(self.name)
...    	   
>>> s1,s2=Student('u1'),Student('u2')
>>> # 对属性进行了限制,无法添加
>>> s1.name2='实例属性'
Traceback (most recent call last):
  File "<interactive input>", line 1, in <module>
AttributeError: 'Student' object has no attribute 'name2'

通过类方法创建类属性

>>> Student.method()
>>> dir(Student)
[..., 'age', 'method', 'method1', 'name', 'num']
>>> dir(s1)
[..., 'age', 'method', 'method1', 'name', 'num']
>>> dir(s2)
[..., 'age', 'method', 'method1', 'name', 'num']

通过类方法,给类重新定义了一个age属性,这里有个问题,实例属性name出现在了类维护的__dict__字典中,这是由于在类中添加了__slots__=(‘name’,)限制,这里的name和实例的name不是一样的,如下:

>>> Student.name
<member 'name' of 'Student' objects>
>>> s1.name
'u1'
>>> s2.name
'u2'

属性的访问限制

为了限制属性的访问域,java中提供了多种访问修饰符,python中通过下划线来划分

#     _ "单下划线" 开始的成员变量叫做保护变量,
#         意思是只有类对象(即类实例)和子类对象自己能访问到这些变量,
#         需通过类提供的接口进行访问;不能用'from module import *'导入
#     __ "双下划线" 开始的是私有成员,意思是只有类对象自己能访问,连子类对象也不能访问到这个数据。
#     __xxx__ 也就是以双下划线开头,并且以双下划线结尾的,是特殊变量,特殊变量是可以直接访问的,不是private变量

将上面的Student类修改一下,将num属性前加上双下划线__num,然后通过类名访问,如下:

>>> class Student():
...    __slots__=('name',)
...    __num = '类属性'
...    def __init__(self,name):
...        self.name = name
...    @classmethod
...    def method(cls):
...        cls.age = '类属性'
...    def method1(self):
...    	   print(self.name)
... 
>>> Student.__num
Traceback (most recent call last):
  File "<interactive input>", line 1, in <module>
AttributeError: type object 'Student' has no attribute '__num'   	   

现在无法直接访问该属性了,再看看类维护的__dict__字典,如下:

>>> dir(Student)
['_Student__num', ..., 'method', 'method1', 'name']

会发现,在以前的基础上多了一个_Student__num属性,这个其实就是我们定义的__num属性,如下:

>>> Student._Student__num
'类属性'

但是不建议这样做,因为python非常灵活,因此也很容易出错,所以python会增加一些限制,了解原理只是为了以后更好的解决问题,不要随意破坏这些限制。

@property

在绑定属性时,如果我们直接把属性暴露出去,虽然写起来很简单,但是,没办法检查参数,可能导致属性被随意的修改,从而破坏程序,因此增加属性的访问限制也是为了避免这类问题。
那么如何访问类的私有属性呢?Java中通常是定义getter和setter方法,python中也可以如此,如下:

>>> class Student():
...    __slots__=('name',)
...    __num = '类属性'
...    def __init__(self,name):
...        self.name = name
...    @classmethod
...    def method(cls):
...        cls.age = '类属性'
...    def method1(self):
...    	   print(self.name)
...    @classmethod
...    def setNum(cls,num):
...    	   cls.__num=num
...    @classmethod
...    def getNum(cls):
...    	   return cls.__num
... 
>>> Student.getNum()
'类属性'
>>> Student.setNum('类的私有属性')
>>> Student.getNum()
'类的私有属性'

Student类中新增了setNum和getNum()方法,用来访问__num属性。python中通过@property装饰器提供了更简洁的一种写法,但是是用来修饰实例方法的,如下:

>>> class Student():
... 	@property
... 	def score(self):
... 		return self.__score
... 	@score.setter
... 	def score(self,score):
... 		self.__score=score
... 
>>> s1.score=80
>>> s1.score
80

通过@property装饰器将方法变成了可调用属性,可以使用更简洁的方式访问到私有属性。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值