目录
类的封装
封装是什么
封装就是把东西放在一起然后封起来,别人就拿不到了
封装的两个层面
第一个层面
- 第一个层面的封装(什么都不用做):创建类和对象会分别创建二者的名称空间,我们只能用类名.或者obj.的方式去访问里面的名字,这本身就是一种封装
第二个层面
- 第二个层面的封装:类中把某些属性和方法隐藏起来(或者说定义成私有的),只在类的内部使用、外部无法访问,或者留下少量接口(函数)供外部访问。
在python中用双下划线的方式实现隐藏属性(设置成私有的)
类中所有双下划线开头的名称如__x都会自动变形成:_类名__x的形式:
class A:
__count = 0
def __init__(self,name):
self.name = name
def __f1(self):
print('from A.__f1')
A.__count += 1
print(A.__count)
def bar(self):
self.__f1()
a = A('hades')
print(a.__count) # 报错,拿不到__count,这是封装在类内部,外部不能调用
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-59-3b81c13cf856> in <module>
15
16 a = A('hades')
---> 17 print(a.__count) # 报错,拿不到__count,这是封装在类内部,外部不能调用
18
AttributeError: 'A' object has no attribute '__count'
a.bar() # 这是在类内部进行操作,才可以拿到
from A.__f1
1
- 注意:对于这一层面的封装(隐藏),我们需要在类中定义一个函数(接口函数)在它内部访问被隐藏的属性,然后外部就可以使用了
这种变形需要注意的是:
- 这种机制并没有真正意义上的限制我们从外部直接访问属性,我们可以通过
实例化对象名._类名__属性名
进行访问
a._A__count += 10 # 此时的_A__count是对象a的属性,相当于a._A_count = a._A__count + 10
# 此时等号后面的a._A__count会先去a这个对象进行查找_A__count,找不到就去类里面找,找到了就进行运算
print(a.__dict__)
print(a._A__count)
{'name': 'hades', '_A__count': 11}
11
a._A__f1() # 这个访问的是A._A__count
from A.__f1
2
- 变形的过程只在类的定义的时候发生一次,在定义后的赋值操作,不会变形
私有模块
同样的,我们模块也是有私有属性的,也是通过加下划线的方式进行封装,但只是一个下划线
# m1.py
_X = 10
y = 20
# m2.py
from m1 import *
print(y) # 20
print(_X) 进行报错
from m1 import _X
print(_X) # 10
类的property特性
什么是property特性
- property装饰器用于将被装饰的方法伪装成一个数据类型,在使用的时候可以不加括号而直接使用
注意:这种只适合类内部除了self参数外没有其他参数的函数
class Foo:
def func(self):
print('from func')
@property
def pro(self):
print('from pro')
f = Foo()
f.func() # 正常调用类的方法
f.pro # 将类的方法当成属性使用
from func
from pro
property属性的定义和调用要注意一下几点:
-
定义时,在实例方法的基础上添加 @property 装饰器;并且仅有一个self参数
-
调用时,无需括号
property属性的两种方式
- 装饰器:在方法上应用装饰器
- 类属性:在类中定义值为property对象的类属性
装饰器
-
经典类只具有@property这一种方法
-
新式类中具有@property,@方法名.setter,@方法名.deleter这三种方法
class Goods:
def __init__(self, name):
self.name = name
self.goods_price = 125
@property
def price(self):
print(self.goods_price)
@price.setter
def price(self, value):
self.goods_price = value
print('from good_price')
return self.goods_price
@price.deleter
def price(self):
del self.goods_price
obj = Goods('clothes')
print(obj.__dict__)
obj.price # 自动调用@property装饰的函数
obj.price = 250 # 自动调用@price.setter装饰的函数
obj.price
del obj.price # 自动调用@price.deleter装饰的函数
print(obj.__dict__)
{'name': 'clothes', 'goods_price': 125}
125
from good_price
250
{'name': 'clothes'}
类与对象的绑定方法和非绑定方法
绑定方法
对象的绑定方法
在类中,没有被任何装饰器修饰的方法就是 绑定到对象的方法,这类方法专门为对象定制
class People:
country = 'china'
def __init__(self, name, age):
self.name = name
self.age = age
def discribe(self):
print(f'{self.name} is {self.age} years old')
p = People('hades',27)
p.discribe() # 通过对象进行调用
hades is 27 years old
discribe即为对象的绑定方法,这个方法不在对象的名称空间,而是在类的名称空间中
- 通过对象调用绑定到对象的方法,会有一个自动传值的过程,即自动将当前对象传递给方法的第一个参数(self,一般都叫self,也可以写成别的名称);若是使用类调用,则第一个参数需要手动传值。
People.discribe(p) # 通过类进行调用,就需要把对象传进去
hades is 27 years old
类的绑定方法
- 类中使用 @classmethod 修饰的方法就是绑定到类的方法。这类方法专门为类定制。通过类名调用绑定到类的方法时,会将类本身当做参数传给类方法的第一个参数。
class A:
count = 1
@classmethod
def f1(cls,obj):
print(cls.count)
cls.f2(obj)
def f2(self):
print(self,'from f2')
a = A()
A.f1(a)
1
<__main__.A object at 0x0000024E75EDDBA8> from f2
a.f1(a)
1
<__main__.A object at 0x0000024E75EDDBA8> from f2
非绑定方法
- 在类内部使用 @staticmethod 修饰的方法即为非绑定方法,这类方法和普通定义的函数没有区别,不与类或对象绑定,谁都可以调用,且没有自动传值的效果。
class A:
count = 1
def __init__(self, name, age):
self.name = name
self.age = age
@classmethod
def f1(cls,obj):
print(cls.count)
cls.f2(obj)
@staticmethod
def f2(self):
print(self,'from f2')
a = A('hades',27)
A.f2(a)
a.f2() # 报错,因为没有进行传参,也就证明了这个方法不是对象的绑定方法
<__main__.A object at 0x0000024E760BD198> from f2
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-96-632f4a11ba55> in <module>
19
20 A.f2(a)
---> 21 a.f2() # 报错,因为没有进行传参,也就证明了这个方法不是对象的绑定方法
TypeError: f2() missing 1 required positional argument: 'self'