Python 基础第二十二天(封装)
今日主要内容:
1.封装
2.内置函数
property
classmethod
staticmethod
封装
什么是封装:
在python中就是将类里面的静态变量,属性,方法进行私有化处理,只能类内进行访问,类外无法继承,调用.
例1:对静态变量进行封装,静态变量的私有化
class A: __n = 'aaa' print(a.__n) #错误,静态变量被封装后,不能类外调用 class A: __n = 'aaa' def func(self): print(A.__n) #正确
python中封装/私有化的格式:
python就是把__名字当成私有的语法,__为两个下划线
那么,python中类是否定义了私有化呢?
print(A.__dict__) 结果: {'__module__': '__main__', '_A__N': 'aaa', 'func': <function A.func at 0x0000000001E6B8C8>, '__dict__': <attribute '__dict__' of 'A' objects>, '__weakref__': <attribute '__weakref__' of 'A' objects>, '__doc__': None}
说明:
在类定义了私有化后,python解释器就对__N进行了名称的转换然后存储在内存中,所以在外部如果还用定义的名称定义的话就会报错:
解释器给转换成了:_类名__名字
总结:
一个私有的名字 在存储的过程中仍然会出现在A.__dict__中,所以我们仍然可以调用到。
python对其的名字进行了修改: _类名__名字
只不过在类的外部调用 :需要“_类名__名字”去使用
在类的内部可以正常的使用名字
在类内 只要你的代码遇到__名字,就会被python解释器自动的转换成_类名__名字
例2:属性的私有化
class B: def __init__(self,name): self._name = name def func(self): print('in func:%s' %self._name) b = B('alex') b.func()
例3:私有化的方法:
class C: def __wahaha(self): print('wahaha') c = C() c.__wahaha() #报错:AttributeError: 'C' object has no attribute '__wahaha' class C: def _wahaha(self): print('wahaha') def ADCa(self): self.wahaha() c = C() c.ADCa() #这样就可以访问了
总结:
在类中,静态属性,方法,对象属性都可以变成私有的,只需要在这些名字之前加上__
面试题:
例:考察私有方法继承的问题:
class D: def __init__(self): self.__func() def __func(self): print('in D') class E(D): def __func(self): print('in E') e = E() 结果:in D
私有的名字,在类内使用的时候,就是会变形成_该类名__方法名
以此为例 :没有双下换线会先找E中的func
但是有了双下划线,会在调用这个名字的类D中直接找_D__func
过程如图:
解析:
1.内存中创建内存空间 D,E 2.对E进行实例化,内存中添加 e对象的内存空间
3.实例化的过程中会查找__init__过程,E内没有,所以找到E的父类D,
4.__init__中定义的__func属性,转换成 _D__func()
5.执行这个私有方法,所以打印'in D'
Java和python关于类内方法的比较:
java中的对比
public 公有的 在类的内部可以使用,子类可以使用,外部可以使用. python中所有正常的名字
protect 保护的 在类的内部可以使用,子类可以使用,外部不可以使用. python中没有
private 私有的 只能在类的内部使用,子类和外部都不可以使用
python中私有的用法:
1.当一个方法不想被子类继承的时候
2.有些属性或者方法不希望从外部被调用,只想提供给内部的方法使用
内置函数
property
作用:
将类内的一个方法伪装成一个属性
这样做的好处:
1.并不会让你的代码有什么逻辑上的提高
2.只是从调用者的角度上换了一种方式,使之看起来更合理
例:
#没有使用property from math import pi class Circle: def __init__(self,r): self.r =r def cal_area(self): return self.r ** 2 * pi def cal_perimeter(self): return 2*pi* self.r c = Circle(6) print(c.cal_area()) #类.方法() print(c.cal_perimeter()) #使用property方法对代码进行改进或伪装 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(6) print(c.area) #变成 类.属性名 print(c.perimeter)
结论:
被property装饰的area,perimeter仍然是一个方法 存在Circle.__dict__(存在于类.__dict__中)
对象的.__dict__中不会存储这个属性
在一个类加载的过程中,会先加载这个中的名字,包括被property装饰的
在实例化对象的时候,python解释器会先到类的空间里看看有没有这个被装饰的属性,
如果有就不能再在自己对象的空间中创建这个属性了
property方法的修改和删除
property的修改: @方法名.setter (之前@property装饰的方法名一致,两个装饰方法修饰的方法名称要一致.)
class Person: def __init__(self,n): self.__name = n #私有属性 @property def name(self): return self.__name @name.setter #对定义的name 方法进行修改,两个方法名称可以一致,@name == @property装饰的名字 def name(name,new_name): if type(new_name) is str: #判断填入类型 self.__name = new_name else: print('填入的数据类型错误') p = Person('alex') print(p.name) p.name = 'jason' #操作的是@name.setter下面装饰的方法 print('p.name')
property的删除:@方法名.deleter (之前@property装饰的方法名一致,两个装饰方法修饰的方法名称要一致.)
例:
class Person: def __init__(self,n): self.__nn = n #私有属性 @property def name(self): return self.__nn @name.deleter def name(self): del self.__nn p = Person('alex') print(p.name) del p.name #删除@name.deleter下面装饰的方法 print(p.name) #结论: #AttributeError: 'Person' object has no attribute '_Person__nn'
总结:
@property --> func 将方法伪装成属性,只是用来好看的
@func.setter --> func 对伪装的属性进行赋值的时候调用这个方法 一般情况下用来做修改
@func.deleter --> func 在执行del 对象.func的时候调用这个方法 一般情况下用来做删除 基本不用
作用
将一些需要随着一部分属性的变化而变化的值的计算过程 从方法 伪装成属性
将私有的属性保护起来,让修改的部分增加一些约束,来提高程序的稳定性和数据的安全性
classmethod
作用:
类方法 可以直接被类调用 不需要默认传对象参数 只需要传一个类参数就可以了
例:
class Goods: __discount = 0.8 def __init__(self,name,price): self.name = name self._price = price @property def or_price(self): return self.__price * Goods.__discount @classmethod def ch_discount(cls,new_discount) #cls表示类方法参数,调用时需要类 cls.__discount = new_discount Goods.ch_discount(1) #类函数classmethod的引用,直接填入参数'new_discount' apple = Goods('apple',4) banana = Goods('banana',5) print(apple.or_price) print(banana.or_price)
@classmethod 是一个函数修饰符,它表示接下来的是一个类方法,而对于平常我们见到的则叫做实例方法。 类方法的第一个参数cls,而实例方法的第一个参数是self,表示该类的一个实例。
普通对象方法至少需要一个self参数,代表类对象实例
类方法有类变量cls传入,从而可以用cls做一些相关的处理。并且有子类继承时,调用该类方法时,传入的类变量cls是子类,而非父类。 对于类方法,可以通过类来调用,就像C.f(),有点类似C++中的静态方法, 也可以通过类的一个实例来调用,就像C().f(),这里C(),写成这样之后它就是类的一个实例了
@staticmethod 用法:
当一个方法要使用对象的属性时 就是用普通的方法
当一个方法要使用类中的静态属性时 就是用类方法
当一个方法要既不使用对象的属性也不使用类中的静态属性时,就可以使用staticmethod静态方法
@staticmethod 特点:不需要像类方法或者普通的实例方法一样需要必须的参数(cls或者self).
例:
class Date: def __init__(self,month,day,year) self.mouth = mouth self.day = day self.year = year @staticmethod def is_date_valid(date_as_string): day,month,year = map(int, date_as_string.split('-')) return day <= 31 and month <= 12 and year <= 3999 is_date = Date.is_date_valid('11-09-2012') #此处调用 staticmethod装饰的函数,可以在类外直径传值.不用理睬self参数内的传值.
staticmethod可以像一个普通的方法被调用,它与这个类有明确的相关性,但是不需要访问这个类内部的属性或者方法.
对比:
- @classmethod,由于其强制要求有cls参数存在,可以更多的用于当作一个类实例工厂🏭,或者可以作为一个可以用于派生类中的构造函数;
- @staticmethod,如果一个方法不需要使用类内部的属性和方法,但确实和类有明确的相关性,它就可以使用@staticmethod来修饰
方法调用总结:
使用什么样的方法要看具体用到了哪些名称空间中的变量
# 当一个方法要使用对象的属性时 就是用普通的方法
# 当一个方法要使用类中的静态属性时 就是用类方法
# 当一个方法要既不使用对象的属性也不使用类中的静态属性时,就可以使用staticmethod静态方法