前言:
本篇相关内容分为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
"
)
或者简单版:

