文章目录
1、封装
1.1、定义
1、封装就是把数据与功能都整合到一起,“整合”二字其实就是封装的通俗说法
2、在封装的基础上,我们可以将装到对象或者类中的属性给隐藏起来
注意:
(1) 在定义类或者初始化对象时,在属性前加__,就会将该属性隐藏起来
但该隐藏起始只是一种变形_类名__属性名,并没有真的隐藏起来
(2) 该变形操作是在类定义阶段扫描语法时发生的变形,类定义之后添加的__开头的属性不会发生变形
(3) 该隐藏是对外不对内
(4) 在继承中,父类如果不想让子类覆盖自己的方法,可以将方法定义为私有的
1.2、使用
class Student:
__school = 'ml' # 隐藏属性 _Student__school
def __init__(self, name, age, gender):
self.name = name
self.__age = age # _Student__age
self.gender = gender
# 相同的功能
def choose(self):
print('{}正在选课'.format(self.name))
stu_obj = Student('allen', 18, 'male')
# 隐藏属性其实也是保存在字典中,只不过存储的属性名字变形了,变成了_类名__属性名
print(stu_obj.__dict__) # {'name': 'allen', '_Student__age': 18, 'gender': 'male'}
print(stu_obj._Student__school) # ml
print(stu_obj._Student__age) # 18
# 类定义之后添加的__开头的属性不会发生变形
stu_obj.__xxx = 111
print(stu_obj.__dict__) # {'name': 'allen', '_Student__age': 18, 'gender': 'male', '__xxx': 111}
1.3、隐藏属性的意义
1.3.1、对外提供数据接口
# 把数据属性隐藏起来的意义是: 在类内开放接口,让外界使用者通过接口来操作属性值,我们可以在接口之上附加任意的逻辑,来严格控制外界使用者对属性的操作
class Student:
def __init__(self, name, age, gender):
self.name = name
self.__age = age # _Student__age
self.gender = gender
# 对外提供接口访问年龄属性
def get_age(self):
return self.__age # 隐藏属性对外不对内
def set_age(self, value):
# 我们可以在接口之上附加任意的逻辑,来严格控制外界使用者对属性的操作
if not isinstance(value, int):
print('年龄必须是正整数')
return
self.__age = value
def del_age(self):
print('不允许删除')
stu_obj = Student('allen', 18, 'male')
print(stu_obj.age) # 报错 没有该属性
print(stu_obj.__age) # 报错 没有该属性
print(stu_obj.get_age()) # 18
stu_obj.set_age('aaa') # 为age添加属性类型是字符串 接口返回: 年龄必须是正整数
print(stu_obj._Student__age) # age属性没有修改成功,仍然是18
stu_obj.set_age(19) # 接口中逻辑检测通过,年龄修改成功
print(stu_obj._Student__age) # 19
stu_obj.del_age() # 不允许删除
1.3.2、把功能属性隐藏起来:隔离复杂度
# 将类中不希望直接给用户访问的属性和方法定义为隐藏属性,对外提供接口给用户访问
class ATM:
def __card(self):
print('插卡')
def __auth(self):
print('用户认证')
def __input(self):
print('输入取款金额')
def __print_bill(self):
print('打印账单')
def __take_money(self):
print('取款')
def withdraw(self):
self.__card()
self.__auth()
self.__input()
self.__print_bill()
self.__take_money()
obj = ATM()
obj.withdraw()
1.4、property
# python专门提供了一个装饰器property,可以将类中的函数“伪装成”对象的数据属性,对象在访问该特殊属性时会触发功能的执行,然后将返回值作为本次访问的结果
# 使用property有效地保证了属性访问的一致性
class Person(object):
def __init__(self, name, height, weight):
self.name = name
self.height = height
self.weight = weight
@property
def bmi(self):
return self.weight / (self.height ** 2)
p_obj = Person('allen', 1.82, 68)
print(p_obj.bmi) # 使用property装饰后访问方法直接可以使用点属性的方法访问
针对property提供获取、设置和删除属性的功能
1.4.1、方式一
class Student:
def __init__(self, name, age, gender):
self.name = name
self.__age = age
self.gender = gender
def get_age(self):
return self.__age
def set_age(self, value):
self.__age = value
def del_age(self):
print('不可以删除')
age = property(get_age, set_age, del_age)
stu_obj = Student('allen', 18, 'male')
# 获取属性
print(stu_obj.age) # 18
# 修改属性
stu_obj.age = 19
print(stu_obj.age) # 19
# 删除属性
del stu_obj.age # 不可以删除
print(stu_obj.age) # 19
1.4.2、方式二
class Student:
def __init__(self, name, age, gender):
self.name = name
self.__age = age
self.gender = gender
@property
def age(self):
return self.__age
@age.setter
def age(self, value):
self.__age = value
@age.deleter
def age(self):
print('不可以删除')
stu_obj = Student('allen', 18, 'male')
# 获取属性
print(stu_obj.age) # 18
# 修改属性
stu_obj.age = 19
print(stu_obj.age) # 19
# 删除属性
del stu_obj.age # 不可以删除
print(stu_obj.age) # 19
1.5、绑定方法
1.5.1、定义
======类中的定义的函数======
绑定方法: 谁来调用就会将谁当作第一个参数传入
(1)绑定给对象的方法: 类中定义的函数默认就是绑定给对象的方法,应该是由对象调用,会把对象当作第一个参数传入
(2)绑定给类的方法: 在类中的函数上加一个装饰器@classmethod,该函数就绑定给类了,应该是由类来调用,会把类当作第一个参数传入
1.5.2、绑定方法的使用
class Student:
def __init__(self, name, age, gender):
self.name = name
self.age = age
self.gender = gender
# 绑定给对象的方法
def choose(self):
print('{}正在选课'.format(self.name))
# 绑定给类的方法
@classmethod
def f1(cls):
print(cls) # 哪个类来调用该方法就打印哪个类
stu_obj = Student('allen', 18, 'male')
stu_obj.choose() # allen正在选课
Student.f1() # <class '__main__.Student'>
# 用对象调用绑定给类的方法得到的还是对象所在的类
stu_obj.f1() # <class '__main__.Student'>
1.6、非绑定方法
1.6.1、定义
======类中的定义的函数======
非绑定方法: 既不与类绑定也不与对象绑定,就是一个普通的函数,谁都可以来调用,没有自动传参的效果,在函数上添加装饰器@staticmethod
1.6.2、非绑定方法的使用
class Student:
def __init__(self, name, age, gender):
self.name = name
self.age = age
self.gender = gender
# 非绑定方法,类可以调用,对象也可以调用,没有自动传参的效果
@staticmethod
def f2(x, y):
print(x, y)
stu_obj = Student('allen', 18, 'male')
print(Student.f2) # <function Student.f2 at 0x00000274369DB4C0>
print(stu_obj.f2) # <function Student.f2 at 0x00000274369DB4C0>
Student.f2(1, 2) # 1 2
stu_obj.f2(1, 2) # 1 2
1.7、绑定方法与非绑定方法综合案例
# 获取MySQL的主机与端口号
import uuid
class MySQL:
def __init__(self, host, port):
self._host = host
self._port = port
self._uid = self.__create_uid()
def check_detail(self):
return '主机名: {} 端口: {} id号: {}'.format(self._host, self._port, self._uid)
# 该方法用不到对象也用不到类,定义为静态方法
@staticmethod
def __create_uid():
return uuid.uuid4()
@classmethod
def from_conf(cls):
import settings
return cls(settings.host, settings.port) # 在类方法中完成实例化
# 调用方式一: 实例化类给出参数
obj = MySQL('127.0.0.1', 3306)
print(obj.check_detail()) # 主机名: 127.0.0.1 端口: 3306 id号: d7785e10-c0cf-4d0b-a89a-8979d2f26c4b
# 调用方式二: 调用类方法,在类方法中实例化
cls_obj = MySQL.from_conf()
print(cls_obj.check_detail()) # 主机名: 127.0.0.1 端口: 3306 id号: e490e7e8-bbb7-4585-ad59-cea8b5f89f5c
# settings模块
host = '127.0.0.1'
port = 3306