Python面向对象之封装!老男孩Python学习内容
老男孩IT教育
常见问题
2021年7月19日 17:58
封装是指隐藏对象的属性和实现细节,仅对外提供公共访问方式;我们程序设计追求“高内聚,低耦合”。
封装是指隐藏对象的属性和实现细节,仅对外提供公共访问方式。
我们程序设计追求“高内聚,低耦合”
高内聚:类的内部数据操作细节自己完成,不允许外部干涉
低耦合:仅对外暴露少量的方法用于使用。
隐藏对象内部的复杂性,只对外公开简单的接口。便于外界调用,从而提高系统的可扩展性、可维护性。通俗的说,把该隐藏的隐藏起来,该暴露的暴露岀来。这就是封装性的设计思想。
私有属性
在class内部,可以有属性和方法,而外部代码可以通过直接调用实例变量的方法来操作数据,这样,就隐藏了内部的复杂逻辑。但是,从前面Student类的定义来看,外部代码还是可以自由地修改一个实例的name、score属性:
class Student(object):
def __init__(self, name, score):
self.name = name
self.score = score
alvin=Student("alvin",66)
yuan=Student("yuan",88)
alvin.score=100
print(alvin.score)
如果要让内部属性不被外部访问,可以把属性的名称前加上两个下划线__,在Python中,实例的变量名如果以__开头,就变成了一个私有变量(private),只有内部可以访问,外部不能访问,所以,我们把Student类改一改:
class Student(object):
def __init__(self, name, score):
self.name = name
self.__score = score
alvin = Student("alvin",66)
yuan = Student("yuan",88)
print(alvin.__score)
改完后,对于外部代码来说,没什么变动,但是已经无法从外部访问实例变量.__name和实例变量.__score。
这样就确保了外部代码不能随意修改对象内部的状态,这样通过访问限制的保护,代码更加健壮。
但是如果外部代码要获取name和score怎么办?可以给Student类增加get_score这样的方法:
class Student(object):
def __init__(self, name, score):
self.name = name
self.__score = score
def get_score(self):
return self.__score
alvin=Student("alvin",66)
yuan=Student("yuan",88)
print(alvin.get_score())
如果又要允许外部代码修改age怎么办?可以再给Student类增加set_score方法:
class Student(object):
def __init__(self, name, score):
self.name = name
self.__score = score
def get_score(self):
return self.__score
def set_score(self,score):
self.__score = score
alvin=Student("alvin",12)
print(alvin.get_score())
alvin.set_score(100)
print(alvin.get_score())
你也许会问,原先那种直接通过alvin.score = 100也可以修改啊,为什么要定义一个方法大费周折?因为在方法中,可以设置值时做其他操作,比如记录操作日志,对参数做检查,避免传入无效的参数等等:
class Student(object):
...
def set_score(self,score):
if isinstance(score,int) and 0 <= score <= 100:
self.__score = score
else:
raise ValueError('error!')
注意
1、这种机制也并没有真正意义上限制我们从外部直接访问属性,知道了类名和属性名就可以拼出名字:
_类名__属性,然后就可以访问了,如a._A__N
2、变形的过程只在类的内部生效,在定义后的赋值操作,不会变形
class Student(object):
def __init__(self, name, score):
self.name = name
self.__score = score
def get_score(self):
return self.__score
yuan=Student("yuan",66)
print(yuan.__dict__)
yuan.__age=18
print(yuan.__dict__)
4、单下划线、双下划线、头尾双下划线说明:
__foo__: 定义的是特殊方法,一般是系统定义名字 ,类似 __init__() 之类的。
_foo: 以单下划线开头的表示的是 protected 类型的变量,即保护类型只能允许其本身与子类进行访问。(约定成俗,不限语法)
__foo: 双下划线的表示的是私有类型(private)的变量, 只能是允许这个类本身进行访问了。
class Person(object):
def __init__(self, name, score):
self.name = name
self.__score = score
class Student(Person):
def get_score(self):
return self.__score
def set_score(self,score):
self.__score=score
yuan=Student("yuan",66)
print(yuan.__dict__)
print(yuan.get_score())
私有方法
在继承中,父类如果不想让子类覆盖自己的方法,可以将方法定义为私有的:
class Base:
def foo(self):
print("foo from Base")
def test(self):
self.foo()
class Son(Base):
def foo(self):
print("foo from Son")
s=Son()
s.test()
class Base:
def __foo(self):
print("foo from Base")
def test(self):
self.__foo()
class Son(Base):
def __foo(self):
print("foo from Son")
s=Son()
s.test()
property属性装饰器
使用接口函数获取修改数据 和 使用点方法设置数据相比, 点方法使用更方便,我们有什么方法达到 既能使用点方法,同时又能让点方法直接调用到我们的接口了,答案就是property属性装饰器:
class Student(object):
def __init__(self,name,score,sex):
self.__name = name
self.__score = score
self.__sex = sex
@property
def name(self):
return self.__name
@name.setter
def name(self,name):
if len(name) > 1 :
self.__name = name
else:
print("name的长度必须要大于1个长度")
@property
def score(self):
return self.__score
# @score.setter
# def score(self, score):
# if score > 0 and score < 100:
# self.__score = score
# else:
# print("输入错误!")
yuan = Student('yuan',18,'male')
yuan.name = '苑昊' # 调用了score(self, score)函数设置数据
print(yuan.name) # 调用了score(self)函数获取数据
yuan.score = 199
print(yuan.score)
注意,使用 @property 装饰器时,接口名不必与属性名相同。
python提供了更加人性化的操作,可以通过限制方式完成只读、只写、读写、删除等各种操作
class Person:
def __init__(self, name):
self.__name = name
def __get_name(self):
return self.__name
def __set_name(self, name):
self.__name = name
def __del_name(self):
del self.__name
# property()中定义了读取、赋值、删除的操作
# name = property(__get_name, __set_name, __del_name)
name = property(__get_name, __set_name)
yuan = Person("yuan")
print(yuan.name) # 合法:调用__get_name
yuan.name = "苑昊" # 合法:调用__set_name
print(yuan.name)
# property中没有添加__del_name函数,所以不能删除指定的属性
del p.name # 错误:AttributeError: can't delete Attribute
@property广泛应用在类的定义中,可以让调用者写出简短的代码,同时保证对参数进行必要的检查,这样,程序运行时就减少了出错的可能性。
老男孩Python全栈开发课程内容主要包括:Python开发基础、Python高级编程与数据库开发、前端开发、WEB框架开发、爬虫开发、全栈项目实战、数据分析、人工智能、自动化运维与开发以及高并发语言GO开发等,独家前沿课程内容,使学员找工作更有优势!
推荐阅读:
