python之路---封装
【封装】
隐藏对象的属性和实现细节,仅对外提供公共访问方式。
【好处】
1. 将变化隔离;
2. 便于使用;
3. 提高复用性;
4. 提高安全性;
【封装原则】
1. 将不需要对外提供的内容都隐藏起来;
2. 把属性都隐藏,提供公共方法对其访问。
私有变量和私有方法
私有变量
class Person: def __init__(self,name): self.__name = name def __eat(self): print( "{} is pig".format(self.__name)) # 类里可以使用私有属性 p = Person('femgyu') # print(p.__name) # 无法调用私有方法 print(p._Person__name) # fengyu 变形后可以调用,但是这种方法不提倡,因为私有属性或方法就不应该在类外面调用 # p.__eat() # 无法调用私有方法 p._Person__eat() # femgyu is pig 变形后可以调用,但是这种方法不提倡,因为私有属性或方法就不应该在类外面调用
这种自动变形的特点:
1.类中定义的__x只能在内部使用,如self.__x,引用的就是变形的结果。
2.这种变形其实正是针对外部的变形,在外部是无法通过__x这个名字访问到的。
3.在子类定义的__x不会覆盖父类定义的__x,因为子类中变形成了:_子类名__x,而父类中变形成了:_父类名__x,即双下滑线开头的属性在继承给子类时,子类是无法覆盖的。
针对上面的3,有一道经典面试题
class A: def __init__(self): self.__func() def __func(self): print('A') class B(A): def __func(self): print('B') B() # 对比下面的代码 class A: def __init__(self): self.func() def func(self): print('A') class B(A): def func(self): print('B') B()
这种变形需要注意的问题是:
1.这种机制也并没有真正意义上限制我们从外部直接访问属性,知道了类名和属性名就可以拼出名字:_类名__属性,然后就可以访问了,如a._A__N
2.变形的过程只在类的内部定义私有属性(或方法)时生效,在类外部定义私有属性(或方法)时,不会变形
class Person: def __init__(self,name): self.__name = name def __eat(self): print( "{} is pig".format(self.__name)) # 类里可以使用私有属性 p = Person('femgyu') print(p.__dict__) p.__age = 20 print(p.__dict__) # 打印结果如下: {'_Person__name': 'femgyu'} {'_Person__name': 'femgyu', '__age': 20}
私有方法
在继承中,父类如果不想让子类覆盖自己的方法,可以将方法定义为私有的
#正常情况 >>> class A: ... def fa(self): ... print('from A') ... def test(self): ... self.fa() ... >>> class B(A): ... def fa(self): ... print('from B') ... >>> b=B() >>> b.test() from B #把fa定义成私有的,即__fa >>> class A: ... def __fa(self): #在定义时就变形为_A__fa ... print('from A') ... def test(self): ... self.__fa() #只会与自己所在的类为准,即调用_A__fa ... >>> class B(A): ... def __fa(self): ... print('from B') ... >>> b=B() >>> b.test() from A
三个装饰器函数
property属性 将方法伪装成属性
在绑定属性时,如果我们直接把属性暴露出去,虽然写起来很简单,但是,没办法检查参数,导致可以把成绩随便改:
class Student(object): def get_score(self): return self.score def set_score(self, value): if not isinstance(value, int): raise ValueError('score must be an integer!') if value < 0 or value > 100: raise ValueError('score must between 0 ~ 100!') self.score = value s = Student() s.set_score(99) print(s.get_score()) # 可以任意修改分数 s.score = 1000 print(s.get_score())
这显然不合逻辑。
class Student(object): def get_score(self): return self._score def set_score(self, value): if not isinstance(value, int): raise ValueError('score must be an integer!') if value < 0 or value > 100: raise ValueError('score must between 0 ~ 100!') self._score = value s = Student() s.set_score(60) print(s.get_score()) # 设为私有属性后,就不可以随便修改分数了 s.__score = 1000 print(s.get_score())
但是,上面的调用方法略显复杂,没有直接用属性这么直接简单。
还记得装饰器(decorator)可以给函数动态加上功能吗?对于类的方法,装饰器一样起作用。Python内置的@property
装饰器就是负责把一个方法变成属性调用的:
@property的实现比较复杂,我们先考察如何使用:
把一个getter方法变成属性,只需要加上@property
就可以了,
此时,@property
本身又创建了另一个装饰器@score.setter
,负责把一个setter方法变成属性赋值。
class Student(object): @property def score(self): return self._score @score.setter def score(self, value): if not isinstance(value, int): raise ValueError('score must be an integer!') if value < 0 or value > 100: raise ValueError('score must between 0 ~ 100!') self._score = value s = Student() # 将方法变成属性进行赋值 s.score = 60 print(s.score) s.__score = 1000 print(s.score)
classmethod (类方法)
类方法:类中的方法传入类cls,不传对象self
类中的方法不需要对象去调用,不需要去实例化一个对象,直接用类名调用
class A(): role = 'dog' @classmethod def func(cls): print(cls.role) A.func()
staticmethod (静态方法)
静态方法:类中的方法不需要传入对象self或者类cls
直接用类名或对象名调用
静态方法就是在类里面定义的普通函数,但也是该类的局部函数。
class A: role = 'dog' @staticmethod def func(): print(A.role) A.func()