封装
广义:把属性或者方法装起来,外面就不能调用了,要通过类的名字去调用
狭义:把属性或者方法藏起来,外面就不能调用了,只能在内部偷偷使用
给一个名字前面加上了双下划线的时候,这个名字就变成了一个私有的
class User: __country = 'China' def __init__(self,name,passwd): self.usr = name self.__pwd = passwd # 私有的实例变量/私有的对象属性 def func(self): print(__country) alex = User('alex','sbsbsb') print(alex.__pwd) # 报错 print(alex.pwd) # 报错 print(User.__country) # 报错 alex.func() # China ''' 解析:设置私有的实例变量或私有的对象属性后,无法通过正常的查看属性方法的方式对其查看。 '''
查看私有实例变量或私有的属性
获取私有实例变量的方法:我们可以在类的内部定义一个方法,把这个私有实例变量作为函数的返回值,这样我们可以通过调用这个方法来获取返回值,从而获取这个私有实例变量。
获取私有方法的方法:我们可以在类的内部定义一个方法,在这个方法中执行这个私有方法,**self.__名称()**,作为返回值直接返回。
import hashlib class User: def __init__(self,name,passwd): self.usr = name self.__pwd = passwd # 私`有的实例变量 def __get_md5(self): # 私有的绑定方法 md5 = hashlib.md5(self.usr.encode('utf-8')) md5.update(self.__pwd.encode('utf-8')) return md5.hexdigest() def getpwd(self): return self.__get_md5() alex = User('alex','sbsbsb') print(alex.getpwd()) ''' 解析:我们在私有方法__get_md5中针对用户名密码加密,当想在类的外部查看时,可以通过对象名.getpwd()查看加密后的结果. '''
私有的原理:
在我们在定义静态变量或方法前面加上__时,在内存中存储的是当前这句话所在的类的名字拼在私有变量,所以我们在外部查不到,而且在外部不能定义私有的概念。而我们在类内部使用的时候自动的把当前这句话所在的类的名字拼在私有变量前完成变形,所以可以进行操作。
class Foo(object): def __init__(self): self.__func() def __func(self): print('in Foo') class Son(Foo): def __func(self): print('in Son') Son() ''' in son 当前这句话所在的类的名字拼在私有变量前完成变形,即__init__所在的类Foo,所以执行的是Foo中的__func '''
注意:私有的内容不能被子类使用!
class Foo(object): def __func(self): print('in Foo') class Son(Foo): def __init__(self): self.__func() Son() ''' 报错 因为在Son空间中没有__func。(只在本类的空间) '''
在其他语言中的数据的级别都有哪些?在python中有哪些? public 公有的 类内类外都能用,父类子类都能用 python支持 protect 保护的 类内能用,父类子类都能用,类外不能用 python不支持 private 私有的 本类的类内部能用,其他地方都不能用 python支持
使用私有的三种情况
让你看让你改
可看不可改 (类 的内部实现)
可看可改 (类 的内部实现)
封装的语法
私有的静态变量
私有的实例变量
私有的绑定方法
私有的特点
可以在类的内部使用
不能在类的外部使用
不能在类的子类中使用
原理
变形:当前这句话所在的类的名字拼在私有变量前完成变形
在类的内部定义的时候变形
类中的变量的级别
共有的 python支持
保护的 python不支持
私有的 python支持
类中的三个装饰器(内置函数)
property
- 把一个方法(不能有参数)伪装成一个属性,例:
from math import pi class Circle: def __init__(self,r): self.r = r @property # 把一个方法伪装成一个属性,在调用这个方法的时候不需要加()就可以直接得到返回值 def area(self): return pi * self.r**2 c1 = Circle(5) print(c1.r) print(c1.area) ''' 解析:@property把一个方法伪装成一个属性,在调用这个方法的时候不需要加()就可以直接得到返回值 如上式中我们想查看c1的面积只需要c1.area就可,省略了() '''
第二个应用场景:和私有的属性合作
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 * self.discount apple = Goods('apple',5) print(apple.price) # 4.0
进阶 —— 对私有属性的更改
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 * self.discount @price.setter # 装饰器 def price(self,new_value): if isinstance(new_value,int): self.__price = new_value @price.deleter def price(self): del self.__price apple = Goods('apple',5) print(apple.price) # 调用的是被@property装饰的price apple.price = 10 # 调用的是被setter装饰的price print(apple.price) del apple.price # 调用的是被deleter装饰的prcie (并不能真正删除什么,而是调用此方法,此方法内部有删除price的代码而已。 ''' 4.0 8.0 '''
classmethod —— 被装饰的方法会成为类方法
作用:把一个对象的绑定方法,修改成一个 类方法
优点:
- 在方法中仍然可以引用类中的静态变量
- 可以不用实例化对象,就直接用类名在外部调用这个方法
- 可以通过类名调用方法,也可以通过对象名调用方法
何时使用:
- 定义了一个方法,默认穿self,但这个self没被使用
- 并且你在这个方法里用到了当前的类名,或者你准备使用这个类的内存空间中的名字的时候
class Goods: __discount = 0.8 def __init__(self): self.__price = 5 self.price = self.__price * self.__discount @classmethod # 把一个对象绑定的方法 修改成一个 类方法 def change_discount(cls,new_discount): cls.__discount = new_discount Goods.change_discount(0.6) # 类方法可以通过类名调用 apple = Goods() print(apple.price) apple.change_discount(0.5) # 类方法可以通过对象名调用 apple2 = Goods() print(apple2.price) ''' 3.0 2.5 '''
例子 一定理解!!
import time class A: def __init__(self,year,month,day): self.year = year self.month = month self.day = day @classmethod def today(cls): year = time.localtime().tm_year month = time.localtime().tm_mon day = time.localtime().tm_mday return cls(year,month,day) t_day = A.today() print(t_day.year,t_day.month,t_day.day) # 2019 6 5
staticmethod —— 被装饰的方法会成为静态方法
作用:把一个普通函数挪到类中来直接使用,制造静态方法用的
何时使用:本身是一个普通的函数,要移动到类的内部执行,那么直接给这个函数添加@staticmethod装饰器就可以了。
在函数的内部既不会用到self变量,也不会用到cls类
class User: pass @staticmethod def login(a,b): # 本身是一个普通的函数,被挪到类的内部执行,那么直接给这个函数添加@staticmethod装饰器就可以了 print('登录的逻辑',a,b) # 在函数的内部既不会用到self变量,也不会用到cls类 obj = User() User.login(1,2) obj.login(3,4) ''' 登录的逻辑 1 2 登录的逻辑 3 4 '''
能定义到类中的内容:
class A: country = '中国' def func(self): print(self.__dict__) @classmethod def clas_func(cls): print(cls) @staticmethod def stat_func(): print('普通函数') @property def name(self): return 'wahaha'
静态变量 — 是个所有对象共享的变量 由对象/类调用。
绑定方法 — 是个自带self参数的函数 由对象调用
类方法 — 是个自带cls参数的函数 由对象/类调用
静态方法 — 是个什么都不带的普通函数 由对象/类调用
property属性:是个伪装成属性的方法 由对象调用,但不加括号
反射 ( getattr() hasattr())
定义:用字符串数据类型的名字,来操作这个名字对应的函数\实例变量\绑定方法等
何时使用:在知道一个变量的字符串数据类型的名字,你想直接调用他,但是你调不到
应用场景:
- 反射 对象的实例变量
- 反射类的 静态变量/绑定方法/其他方法
- 模块中的 所有的变量
- 被导入的模块 模块
- 当前执行的py文件 - 脚本
举例:
- 反射本脚本下类中的方法或实例变量等
class Person: def __init__(self,name,age): self.name = name self.age = age def qqxing(self): print('in qqxing') alex = Person('alex',83) wusir = Person('wusir',74) ret = getattr(alex,'name') print(ret) # alex ret = getattr(wusir,'qqxing') # in qqxing ret() ''' 解析:我们创建一个类alex,当我查看alex中的变量及方法须使用alex.想查看的内容 但如果查看的内容如果是一个待输入的即字符串类型,我们无法执行。 此时,我们可以使用getattr(alex,想查看的内容) 《=》alex.想查看的内容(去除字符串格式后) '''
反射导入的模块中的类
import a print(getattr(a, 'Alipay')) print(getattr(a, 'Wechat')) # 导入模块a中的类的内存地址,可以在其后加()按照格式创建对象
反射当前脚本下的类,函数名,变量等
import sys s1 = '加油老哥们!' class Person: def __init__(self,name,age): self.name = name self.age = age print(getattr(sys.modules['__main__'],'s1')) ret = getattr(sys.modules['__main__'],'Person') nie = ret('alex',18) print(nie.__dict__)
hasattr()
当我们使用getattr()时,如果找不到就会报错,所以在使用之前应该先判断我们要寻找的对应的方法或者变量是否存在。
callable 当我们想对我们输入的内容进行操作,但不知道其对应的是方法,函数还是变量,所以此时我们需判断其是否可调用,可调用就加()在后,否则不加
class A: Role = '治疗' def __init__(self): self.name = 'alex' self.age = 84 def func(self): print('wahaha') a = A() print(hasattr(a,'sex')) # False print(hasattr(a,'age')) # True print(hasattr(a,'func')) # True if hasattr(a,'func'): ————》此时为True if callable(getattr(a,'func')): ————》func为方法,可调用 getattr(a,'func')() ————》 所以调用func
- 一些内置的魔术方法
__new__
!!!!! (构造方法)作用:创建一个对象需要的空间
在实例化的时候先创建一块对象空间,有一个类指针指向类 ——>
__new__
,然后再调用__init__
.class A: def __new__(cls, *args, **kwargs): # 构造方法 # o = super().__new__(cls) o = object.__new__(cls) # 寻找父类中__new__方法并创建一个class A的对象 print('执行new',o) return o # 返回在父类中得到的对象 def __init__(self): # __init__接收到__new__返回的对象给予self print('执行init',self) A() ''' 解析:当我们实例化A时,总是先执行__new__方法,而方法中的内容为寻找并执行父类(object类)的__new__方法,把返回的对象赋予给__Init__的self并执行__init__的函数。 '''
设计模式 —— 单例模式(一个类 从头到尾 只会创建一次self的空间)
\class A: __statu = None def __new__(cls, *args, **kwargs): if not cls.__statu: cls.__statu = super().__new__(cls) return cls.__statu def __init__(self,name,age): self.name = name self.age = age person = A('NIE',18) print(person.name,person.age) # NIE 18 person2 = A('sui',18) print(person.name,person.age) # sui 18 print(person2.name,person2.age) # sui 18 ''' 解析:类中设置了一个私有的静态变量 __statu = None,在进行实例化时先执行A类中的__new__方法,判断__statu是否为空如果为空,则寻找并执行父类的__new__方法,开辟了一个空间,创建了一个A类的对象,并重新赋值给 __statu,当再次实例化时__statu已被赋值,所以不会重新开辟空间创建对象。所以所有A类的对象都使用同一个空间。 '''
python中的 import 导入的都是单例模式
__call__
当判断对象()是否可执行时,使用callable(对象),也就是执行对象内部的
__call__
方法。__len__
获取对象长度。当使用len(对象)时也就是执行对象内部的
__len__
的方法class Cls: def __init__(self,name): self.name = name self.students = [] def len(self): return len(self.students) def __len__(self): return len(self.students) py22 = Cls('py22') py22.students.append('杜相玺') py22.students.append('庄博') py22.students.append('大壮') print(py22.len()) # 3 调用类的绑定方法 print(len(py22)) # 3 调用内置函数
__str__
和__repr__
作用:两者的目的都是为了显式的显示对象的一些必要信息,方便查看和调试。
使用场景:当我想打印对象就想得到对象的某些信息时。此时直接打印无法完成。
注意:
- 只有在print()打印一个对象 用%s进行字符串拼接 或者str(对象)总是调用这个对象的
__str__
方法 __str__
只能返回字符串,所以当返回多个信息时需要进行拼接。
如下例的:‘,‘.join([self.name,str(self.price),self.period])
class Course: def __init__(self,name,price,period): self.name = name self.price = price self.period = period def __str__(self): return ','.join([self.name,str(self.price),self.period]) # 字符串拼接 python = Course('python',21800,'6 months') linux = Course('linux',19800,'5 months') mysql = Course('mysql',12800,'3 months') go = Course('go',15800,'4 months') print(go) # go,15800,4 months print(python) # python,21800,6 months print(mysql) # mysql,12800,3 months lst = [python, linux, mysql, go] for index,c in enumerate(lst,1): print(index,c) num = int(input('>>>')) course = lst[num-1] print('恭喜您选择的课程为 %s 价格%s元'%(course.name,course.price))
- 只有在print()打印一个对象 用%s进行字符串拼接 或者str(对象)总是调用这个对象的
__repr__
__repr__不仅是__str__的替代品,还有自己的功能 用%r进行字符串拼接 或者用repr(对象)的时候总是调用这个对象的__repr__方法
class clas: def __init__(self): self.student = [] def append(self,name): self.student.append(name) def __repr__(self): return str(self.student) def __str__(self): return 'aaa' py22 = clas() py22.append('大壮') print(py22) # aaa 执行__str__ print(str(py22)) # aaa 执行__str__ print('我们py22班 %s'%py22) # 我们py22班 aaa 执行__str__ print('我们py22班 %r'%py22) # 我们py22班 ['大壮'] print(repr(py22)) # ['大壮']