前言:
本篇相关内容分为3篇多态、继承、封装,这篇为第三篇 封装。
Content:
- 封装
1.数据封装和私有属性
2. 类变量和实例变量(对象变量)
3. 类属性和实例属性得查找顺序(MRO)
4. 静态方法 类方法和对象方法使用以及参数
5. python的接口和自省机制
6. 上下文管理器
====================================
1.python的数据封装和私有属性
a.python的__私有属性
python用__开头完成私有属性的封装。用__开头的属性名或者方法就没法直接外部获取,只有类中的公共方法才可以访问。
python的数据封装和java c++这种静态语言不同的是,静态语言其实本身有private类型的。而python是用小技巧实现了这种私有属性。
- 隐藏起一个属性,不想让外部调用
- 保护这个属性,不想让这个属性随意改变
- 保护这个属性不被子类继承
2.类变量和实例变量
a.什么是python的类变量?
类下的变量。
class A(): aa =1
class A: aa =1 def __init__ (self,x,y): self.x = x self.y =y
class Init(object): def __init__ (self, v): # print ( " init " ) self.val = v # print ( " init: " ,self.val) class Add2(Init): def __init__ (self, val): # print ( " Add2 " ) super(Add2, self). __init__ (val) # print ( " add2: " ,self.val) self.val += 2 class Mult(Init): def __init__ (self, val): # print ( " Mult " ) super(Mult, self). __init__ (val) # print ( " Mult: " ,self.val) self.val *= 5 class HaHa(Init): def __init__ (self, val): # print ( " HAHA " ) super(HaHa, self). __init__ (val) # print ( " Haha: " ,self.val) self.val /= 5 class Pro(Add2,Mult,HaHa): # pass class Incr(Pro): def __init__ (self, val): super(Incr, self). __init__ (val) self.val += 1 # Incr Pro Add2 Mult HaHa Init p = Incr(5 ) print (p.val) c = Add2(2 ) print (c.val)
把代码中的print注释都拿掉,可以发现整个流程为:
4. 静态方法 类方法和对象方法使用以及参数
a.静态方法 staticmethod
- 静态方法定义:
使用装饰器@staticmethod。参数随意,没有“self”和“cls”参数,但是方法体中不能使用类或实例的任何属性和方法
- 静态方法的一些特点:
静态方法是类中的函数,不需要实例。
静态方法主要是用来存放逻辑性的代码,主要是一些逻辑属于类,但是和类本身没有交互,即在静态方法中,不会涉及到类中的方法和属性的操作。
可以理解为将静态方法存在此类的名称空间中。事实上,在python引入静态方法之前,通常是在全局名称空间中创建函数。-
- 静态方法调用:
例:我要在类中实现一个得到现在时间的方法。与传进去的任何参数都无关那种。
import time class TimeTest(object): def __init__ (self, hour, minute, second): self.hour = hour self.minute = minute self.second = second @staticmethod def showTime(): return time.strftime( " %H:%M:%S " , time.localtime()) print (TimeTest.showTime()) t = TimeTest(2, 10, 10 ) nowTime = t.showTime() print (nowTime)
b.类方法
- 类方法定义:
使用装饰器@classmethod。第一个参数必须是当前类对象,该参数名一般约定为“cls”,通过它来传递类的属性和方法(不能传实例的属性和方法)
- 类方法的一些特点:
将类本身作为对象进行操作。
不管这个方式是从实例调用还是从类调用,它都用第一个参数把类传递过来
- 类方法使用:
例:实现一个基类做一个有颜色属性的抽象共性,对于实际的颜色的值需要结合实际子类传递的值进行匹配
class ColorTest(object): color = " color " @classmethod def value(self): return self.color class Red(ColorTest): color = " red " class Green(ColorTest): color = " green " g = Green() print (g.value()) print (Green.value())
这时候可能有人会觉得,这个跟实例方法(普通方法)不是一样的嘛,继承父类,并且用继承中的知识重写父类中的属性或者方法?
重点就在于,如果我们把@classmethod这个方法去掉,Green.value()这样去调用是会报错的。
因为需要传递实例参数进去,而不能直接用类调用。
c.对象方法(实例方法)
- 实例方法定义:
第一个参数必须是实例对象,该参数名一般约定为“self”,通过它来传递实例的属性和方法(也可以传类的属性和方法)
- 实例方法的一些特点:
只能由类的实例来调用,就是我们平时最常用的。
比较简单,没有特殊装饰器,暂不举例。
5.python的接口和自省机制
a.什么是python的自省机制
当我们需要实现一个通用的DBM框架时,可能需要对数据对象的字段赋值,但我们无法预知用到这个框架的数据对象都有些什么字段,换言之,我们在写框架的时候需要通过某种机制访问未知的属性。
也就是说,我们需要在很多时候去访问python自己为我们做了哪些隐藏的事情,或者某个框架里具体实现了哪些方法等。
通过python的自省机制去让python告诉我们,我们查询的对象是什么,有哪些功能等。
b.python自省之访问对象的属性
例有下面这个类,并且实例化了一个a对象。
class Company(object): def __init__ (self,company_name,staffs= []): self.company_name = company_name self.staffs = staffs def add(self,staff): self.staffs.append(staff) def remove(self,staff): self.staffs.remove(staff) user_list =[ ' tangrong1 ' , ' tangrong2 ' , ' tangrong3 ' ] a =Company( " aaa " ,user_list)
我需要去得到类里的一些方法和属性:
# ###访问对象的属性 # dir() 调用这个方法将返回包含obj大多数属性名的列表(会有一些特殊的属性不包含在内)。obj的默认值是当前的模块对象。 print ( " dir() " ) print (dir(Company)) print (dir(a)) # hasattr(obj,attr) 这个方法用于检查obj是否有一个名为attr的值的属性,返回一个布尔值 print ( " hasattr() " ) print (hasattr(a, " add " )) print (hasattr(a, " staffs " )) print (hasattr(a, " a " )) # getattr(obj, attr) 调用这个方法将返回obj中名为attr值的属性的值,例如如果attr为'staffs',则返回obj.staffs。 print ( " getattr() " ) print (getattr(a, " add " )) print (getattr(a, " staffs " )) # setattr(obj, attr, val) 调用这个方法将给obj的名为attr的值的属性赋值为val。例如如果attr为'bar',则相当于obj.bar = val。 print ( " setattr() " ) print (setattr(a, " staffs " ,[ " tangrong4 " , " tangrong5 " ])) print (a.staffs)
输出为:
dir() ##ps:可以发现,实例和类的dir()列出来的有些不一样 [ ' __class__ ' , ' __delattr__ ' , ' __dict__ ' , ' __dir__ ' , ' __doc__ ' , ' __eq__ ' , ' __format__ ' , ' __ge__ ' , ' __getattribute__ ' , ' __gt__ ' , ' __hash__ ' , ' __init__ ' , ' __init_subclass__ ' ,
' __le__ ' , ' __lt__ ' , ' __module__ ' , ' __ne__ ' , ' __new__ ' , ' __reduce__ ' , ' __reduce_ex__ ' , ' __repr__ ' , ' __setattr__ ' , ' __sizeof__ ' , ' __str__ ' , ' __subclasshook__ ' , ' __weakref__ ' ,
' add ' , ' remove ' ] [ ' __class__ ' , ' __delattr__ ' , ' __dict__ ' , ' __dir__ ' , ' __doc__ ' , ' __eq__ ' , ' __format__ ' , ' __ge__ ' , ' __getattribute__ ' , ' __gt__ ' , ' __hash__ ' , ' __init__ ' , ' __init_subclass__ ' ,
' __le__ ' , ' __lt__ ' , ' __module__ ' , ' __ne__ ' , ' __new__ ' , ' __reduce__ ' , ' __reduce_ex__ ' , ' __repr__ ' , ' __setattr__ ' , ' __sizeof__ ' , ' __str__ ' , ' __subclasshook__ ' , ' __weakref__ ' ,
' add ' , ' company_name ' , ' remove ' , ' staffs ' ]
hasattr() True True False
getattr()< __main__ .Company object at 0x000002D3172F2518>> [ ' tangrong1 ' , ' tangrong2 ' , ' tangrong3 ' ]
setattr() None [ ' tangrong4 ' , ' tangrong5 ' ]
c.python自省之访问对象的元数据
包括各种__dict__()\__doc__\__bases__等
内容比较多,可看这篇:https://www.cnblogs.com/huxi/archive/2011/01/02/1924317.html
6.上下文管理器
a.什么是上下文管理器?
上下文管理器就是实现了上下文管理协议的对象。主要用于保存和恢复各种全局状态,关闭文件等,上下文管理器本身就是一种装饰器。
感觉上句很像白说是不是- -。实际例就是,类似于with语句,就是遵循了上下文管理协议,才能在内部我们看不到的地方,帮我们完成了退出时做出关闭文件、执行自定义代码块的操作的。就不用我们显示判断调用读取完了就关闭文件这种操作。
with open("test/test.txt","w") as f_obj: f_obj.write("hello")
b.用with看他遵循的上下文管理协议
上下文管理协议包括两个方法:
-
contextmanager.__enter__()
从该方法进入运行时上下文,并返回当前对象或者与运行时上下文相关的其他对象。如果with语句有as关键词存在,返回值会绑定在as后的变量上。 -
contextmanager.__exit__(exc_type, exc_val, exc_tb)
退出运行时上下文,并返回一个布尔值标示是否有需要处理的异常。如果在执行with语句体时发生异常,那退出时参数会包括异常类型、异常值、异常追踪信息,否则,3个参数都是None。
能用with语句的对象,也是因为这个对象里,遵循了这个协议。
with语句就是为支持上下文管理器而存在的,使用上下文管理协议的方法包裹一个代码块(with语句体)的执行,并为try...except...finally提供了一个方便使用的封装。
我们创建一个能支持with(上下文管理协议)的类,这个类实现了db最开始建立连接,退出时关闭连接的操作。
import sqlite3 class DataConn: def __init__ (self,db_name): self.db_name = db_name def __enter__ (self): self.conn = sqlite3.connect(self.db_name) return self.conn def __exit__ (self,exc_type,exc_val,exc_tb): self.conn.close() if exc_val: raise if __name__ == " __main__ " : db = " test/test.db " with DataConn(db) as conn: cursor = conn.cursor()
c.用contextlib自定义上下文管理器
from contextlib import contextmanager @contextmanager def file_open(path): try : f_obj = open(path, " w " ) yield f_obj except OSError: print ( " We had an error! " ) finally : print ( " Closing file " ) f_obj.close() if __name__ == " __main__ " : with file_open( " test/test.txt " ) as fobj: fobj.write( " Testing context managers " )
或者简单版: