发布时间:2018-04-17 20:20:30编辑:Run阅读(3593)
python中的封装
隐藏对象的属性和实现细节,仅对外提供公共访问方式
好处:
1 将变化隔离
2 便于使用
3 提供复用性
4 提高安全性
封装原则
1 将不需要对外提供的内容都隐藏起来
2 把属性都隐藏,提供公共方法对其访问
私有变量和私有方法
在python中用双下划开头的方式将属性隐藏来(设置成私有的)
函数和属性装到了一个非全局的命名空间--封装
私有变量,错误示例
class A: __N = 'aaa' # 静态变量 print(A.__N)
执行报错
AttributeError: type object 'A' has no attribute '__N'
这个__N就是类A的私有属性
定义一个私有的名字:就是在私有的名字前面加两条下划线__N = 'aaa',所谓私有,就是不能在类的外面去引用它
class A: __N = 'aaa' # 静态变量 def func(self): print(A.__N) # 在类的内部使用正常 print(A.__dict__)
可以看到类属性多了一个'_A__N':'aaa'的属性,其实这个_A__N 就是__N,这是python解释器自动做的一个变形操作
类中所有双下划线开头的名称如__x都会自动变形成:_类名__x的形式
例子
class A: __N = 0 # 类的数据属性就应该是共享的,但是语法上是可以把类的数据属性设置成私有的如__N,会变形为_A__N def __init__(self): self.__X = 10 # 变形为self._A__X def __foo(self): # 变形为_A__foo print('from A') def bar(self): self.__foo() # 只有在类内部才可以通过__foo的形式访问到 print(A._A__N) # 是可以访问到的,即这种操作并不是严格意义上的限制外部访问,仅仅只是一种语法意义上的变形 a = A() print(a._A__X) # 是可以访问到的,即这种操作并不是严格意义上的限制外部访问,仅仅只是一种语法意义上的变形 a.bar()
执行结果
0
10
from A
一个私有的名字,在存储的过程中仍然会出现在A.__dict__中,所以我们仍然可以调用到,
python对其的名字进行了修改:_类名__名字
只不过在类的外部调用:需要_类名__名字去使用
在类的内部可以正常的使用名字
在类里面,只要你的代码遇到__名字,就会被python解释器自动的转换成_类名__名字
虽然上面的方式可以访问到私有的,但是却不建议这么做,约定俗成
私有的属性
示例
class B: def __init__(self, name): self.__name = name def func(self): print('in func: {}'.format(self.__name)) # 外部通过调用func来获取类的私有属性 b = B('Sam') b.func()
执行结果
in func: Sam
私有的方法
示例
class C: def __wahaha(self): print('wahaha') def ADCa(self): # 外部通过调用ADCa()方法,来执行类的私有方法__wahaha() self.__wahaha() c = C() c.ADCa()
执行结果
wahaha
在类中,静态属性,方法,对象属性都可以变成私有的,只需要在这些名字之前加上__
例3
class F: pass F.__name = 'Sam' # 误区,不是在创建私有属性 print(F.__name) print(F.__dict__)
执行结果
可以很明确的看到__name并没有发生变形,变形只在类的内部发生,在类的外面创建的双下划线的属性,并不是私有属性
面试题:
1 下面代码执行的结果是什么?为什么?
class D: def __func(self): print('in func') class E(D): def __init__(self): self.__func() e = E()
解答:
class D: def __func(self): # 在类的内部遇到__,python解释器会自动变形成_D__func print('in func') class E(D): def __init__(self): self.__func() # 在类的内部遇到__,python解释器会自动变形成_E__func # 实例化一个对象e,首先会找到E类中的__init__方法 # 遇到了__,所以会去找self._E__func()方法,子类E中没有找到 # 就去父类中找,而父类中的__func变形成_D__func,而执行的是_E__func,所以会报错 e = E()
结果报错
AttributeError: 'E' object has no attribute '_E__func'
2 下面代码执行的结果是什么?为什么?
class D: def __init__(self): self.__func() def __func(self): print('in D') class E(D): def __func(self): print('in E') e = E()
解答
class D: # 第一步加载类D def __init__(self): # 第二步加载__init__ #第七步,由于子类没有init方法,所以找父类的 self.__func() # 第八步执行self.__func(),也就是_D__func def __func(self): # 第三步加载_D__func,双下划线变形 # 第九步,执行_D__func() print('in D') # 第10步打印 'in D' class E(D): # 第四步加载类E def __func(self): # 第五步加载_E__func,双下划线变形 print('in E') e = E() # 第六步,实例化一个对象e ,
执行结果
in D
java中的对比
public 共有的 在类的内部可以使用,子类可以使用,外部可以使用 python类中所有的常规名字
protect 保护的 在类的内部可以使用,子类可以使用,外部不可以使用 python中没有
private 私有的 只能在类的内部使用,子类和外部都不可以使用 python中的__名字
私有的用法
当一个方法不想被子类继承的时候
有些熟悉或者方法不希望从外部被调用,只想提供给内部的方法使用
示例1
描述一个房子,单价,面积,长宽高
class Room: def __init__(self, name, price, length, width, height): # 名字,价格,长,宽,高 self.name = name self.price = price self.__length = length # 私有属性长 self.__width = width # 私有属性宽 self.__height = height # 私有属性高 def area(self): return self.__length * self.__width r = Room('张三', 240000, 11, 5, 2) print('姓名:{},房子价格:{},面积:{}'.format(r.name, r.price, r.area()))
执行结果
姓名:张三,房子价格:240000,面积:55
示例2
用户输入账号名和密码,把密码转换为对应的ascii,最后打印出账号名和转换后的ascii值
class Person: def __init__(self, name, pwd): self.name = name self.__pwd = pwd def __showpwd(self): s = [] for i in self.__pwd: s.append(str(ord(i))) s2 = ''.join(s) return s2 p = Person('zhangsan', '12345') print('账号名为:{},加密后的密码为:{}'.format(p.name, p._Person__showpwd()))
执行结果
账号名为:zhangsan,加密后的密码为:4950515253
property方法
将一个方法伪装成一个属性
1 并不会让你的代码有什么逻辑上的提高
2 只是从调用者的角度上换了一种方式,使之看起来更合理
示例
人体BMI指数
体质指数(BMI)=体重(kg) / 身高**2(m)
写一个类,描述人体BMI指数
class Person: def __init__(self, name, weight, height): self.name = name self.__height = height self.__weight = weight def cal_BMI(self): return self.__weight / self.__height ** 2 @property def bmi(self): return self.__weight / self.__height ** 2 P = Person('张三', 67, 1.71) print(P.bmi)
执行结果
22.91303307000445
在一个类加载的过程中,会先加载这个类中的名字,包括被property装饰的
在实例化对象的时候,python解释器会先到类的空间里看看有没有这个被装饰的属性,如果有就不能在自己对象的空间中创建这个属性了
示例
计算圆形类的,面积,周长,将方法伪装成属性,方法中一般涉及的都是一些计算过程
from math import pi class Circle: def __init__(self, r): self.r = r @property def area(self): return self.r ** 2 * pi @property def perimeter(self): return 2 * pi * self.r c = Circle(10) print(c.area) print(c.perimeter)
执行结果
314.1592653589793
62.83185307179586
例2
class Person: def __init__(self, name): self.__name = name # 定义一个私有名字__name @property def name(self): return self.__name def set_name(self, new_name): if type(new_name) is str: # 判断name的数据类型是否为字符串 self.__name = new_name # 赋值 else: print('你提供的姓名数据类型不合法') p = Person('Sam') print(p.name) p.set_name('Tom') print(p.name)
执行结果
Sam
Tom
@func.setter --> func 对伪装的属性进行赋值的时候调用这个方法,一般情况下用来修改
示例:对方法伪装成的属性进行修改
class Person: def __init__(self, n): self.__name = n # 私有的属性 @property def name(self): return self.__name @name.setter # 使用setter,一定要和property装饰的方法名相同,且要创建同名方法修改 def name(self, new_name): if type(new_name) is str: self.__name = new_name else: print('你提供的姓名数据类型不合法') p = Person('sam') print(p.name) p.name = 'Jack' # 注意,修改使用等号,前面是老的名字,后面是新的名字 print(p.name)
执行结果
sam
Jack
@func.deleter --> func 在执行del 对象.func的时候调用这个方法 一般情况下用来做删除 基本不用
示例
class Person: def __init__(self, n): self.__name = n @property def name(self): return self.__name @name.deleter # 使用deleter也要确保和property装饰的方法同名,且要创建一个相同方法 def name(self): del self.__name p = Person('Sam') print(p.name) del p.name # 删除名字 print(p.name) # 打印报错,因为名字已经被删除了
执行结果
AttributeError: 'Person' object has no attribute '_Person__name'
将一些需要随着一部分属性的变化而变化的值的计算过程 从方法 伪装成属性
将私有的属性保护起来,让修改的部分增加一些约束,来提高程序的稳定性和数据的安全性
示例:有个商品:原价,折扣,当我要查看价格的时候,肯定只看折后的价格
class Goods: def __init__(self, name, origin_price, discount): self.name = name self.__price = origin_price self.__discount = discount @property def price(self): return self.__price * self.__discount @price.setter def price(self, new_price): if type(new_price) is int or type(new_price) is float: self.__price = new_price apple = Goods('apple', 5, 0.8) print(apple.price) # 打折后的价格 apple.price = 10 # 苹果价格上涨 print(apple.price)
执行结果
4.0
8.0
@classmethod
类方法 可以直接被类调用 不需要默认传对象参数 只需要传一个类参数就可以了
示例
class Goods: __discount = 0.8 def __init__(self, name, origin_price): self.name = name self.__price = origin_price @property def price(self): return self.__price * Goods.__discount @classmethod def change_discount(cls, new_discount): cls.__discount = new_discount apple = Goods('apple', 5) banana = Goods('banbana', 8) print(apple.price) print(banana.price) # 折扣价格变了,不打折 # 不依赖对象的方法 就应该定义成类方法 类方法可以任意的操作类中的静态变量 Goods.change_discount(1) # 更改全局静态变量 print(apple.price) print(banana.price)
执行结果
4.0
6.4
5
8
@staticmethod
当一个方法要使用对象的属性时 就是用普通的方法
当一个方法要使用类中的静态属性时 就是用类方法
当一个方法要既不使用对象的属性也不使用类中的静态属性时,就可以使用staticmethod静态方法
示例
class Student: def __init__(self): pass @staticmethod def login(): # login就是一个类中的静态方法 静态方法没有默认参数 就当成普通的函数使用即可 user = input('user:') if user == 'root': print('success') else: print('faild') Student.login()
执行结果
user:root
success
47745
46235
37110
34627
29229
25886
24745
19863
19417
17908
5716°
6315°
5835°
5888°
6984°
5829°
5846°
6361°
6316°
7673°