【python进阶 高级语法 笔记】
目录
1. OOP的属性/方法
1.1. 类属性、实例属性
通过实例理解:
class Province(object):
# 类属性
country = '中国'
def __init__(self, name):
# 实例属性
self.name = name
# 创建一个实例对象
obj = Province('山东省')
# 访问实例属性,需要通过对象来访问
print(obj.name)
# 访问类属性,通过类访问
Province.country
直接定义在类中的属性称为类属性,一般公有的属性作为类属性。类的方法里的变量是实例属性,创建对象时才会创建内存空间。
- 语句 obj = Province('山东省') 会:(1)调用 __new__ 方法创建对象,即创建了一个内存空间;(2)调用 __init__方法,对申请的空间进行初始化;(3)解释器自动把对象的引用给self;
- 通俗的说Province是类对象(一个),类对象的变量即 类属性 ;"山东省" 是实例对象(n个),实例对象的变量即 实例属性。
- country作为类属性,由实例对象共用,实例对象通过 __class__ 属性 记录创建该对象的类的地址。
- 类属性在内存中只保存一份
- 实例属性在每个对象中都要保存一份
- 通过类创建实例对象时,如果每个对象需要具有相同名字的属性,那么就使用类属性,用一份既可
- 实例属性需要通过对象来访问、类属性通过类访问,在使用上可见实例属性和类属性的归属不同
1.2. 实例方法、类方法 和 静态方法
方法包括:实例方法、静态方法 和 类方法,三种方法在内存中都归属于类,区别在于调用方式不同。
- 实例方法:由对象调用;至少一个self参数 (传实例对象的引用);执行实例方法时,自动将调用该方法的对象赋值给self;
- 类方法:由 @classmethod 修饰,由类调用; 至少一个cls参数 (传类对象的引用);执行类方法时,自动将调用该方法的类赋值给cls;
- 静态方法:由 @staticmethod 修饰,由类调用;无默认参数。
- 相同点:对于所有的方法而言,均属于类,所以 在内存中也只保存一份;
- 不同点:方法调用者不同、调用方法时自动传入的参数(传递的引用)不同。
- 注. 简单理解静态方法:当一个函数与class是平等的关系,函数逻辑上与类对象和实例对象无关,可单独拿出来作为普通的函数。但是内容上可能相关,为了方便使用以及不与其他函数混淆,这样的函数可以作为类的静态方法。
- 实例方法访问、修改类属性:通过 对象.__class__.类属性 。
- 类方法访问、修改类属性:传的参数是类对象的引用,可直接改。
- 实例对象可以调用 实例方法、类方法、静态方法;
- 类对象只能调用 类方法、静态方法;
实例:
class Foo(object):
def __init__(self, name):
self.name = name
def ord_func(self):
""" 定义实例方法,至少有一个self参数 """
# print(self.name)
print('实例方法')
@classmethod
def class_func(cls):
""" 定义类方法,至少有一个cls参数 """
print('类方法')
@staticmethod
def static_func():
""" 定义静态方法 ,无默认参数"""
print('静态方法')
f = Foo("中国")
# 调用实例方法
f.ord_func()
# 调用类方法
Foo.class_func()
# 调用静态方法
Foo.static_func()
2. property属性(重点)
2.1. 什么是property属性
property属性一种用起来像是使用的实例属性一样的特殊属性,可以对应于某个方法。使方法调用像是获取变量值一样。如下:
# ############### 定义 ###############
class Foo:
def func(self):
pass
# 定义property属性
@property
def prop(self):
pass
# ############### 调用 ###############
foo_obj = Foo()
foo_obj.func() # 调用实例方法
foo_obj.prop # 调用property属性
- 定义:在实例方法的基础上添加 @property 装饰器;并且仅有一个self参数。
- 调用property属性无需括号 ,使用时不需要考虑传参问题: 对象.方法名 .
Python的property属性的功能是:property属性内部进行一系列的逻辑计算,最终将计算结果返回。
简单的实例:
网页中显示的数据列表页面,每次请求不可能把数据库中的所有内容都显示到页面上,而是通过分页的功能局部显示,所以在向数据库中请求数据时就要显示的指定获取从 第m条到第n条(一页的起始) 的所有数据 这个分页的功能包括:
根据用户请求的当前页和总数据条数计算出一页的起始 m 和 n;
根据m 和 n 去数据库中请求数据。
# ############### 定义 ############### class Pager: def __init__(self, current_page): # 用户当前请求的页码(第一页、第二页...) self.current_page = current_page # 每页默认显示10条数据 self.per_items = 10 @property def start(self): val = (self.current_page - 1) * self.per_items return val # 返回起始值 @property def end(self): val = self.current_page * self.per_items return val # 返回结束值 # ############### 调用 ############### p = Pager(1) # current_page 为 1 p.start # 就是起始值,即:m p.end # 就是结束值,即:n
2.2. 装饰器/类属性 创建property属性
- 装饰器 即:在方法上应用装饰器;
- 类属性 即:在类中定义值为property对象的类属性。
2.2.1 装饰器创建property属性
-
经典类,具有一种@property装饰器:@property ;
-
新式类(继承object),具有三种@property装饰器: @property(取值)、 @方法名.setter(赋值)、@方法名.deleter(删除值),可将三个方法定义为对同一个属性:获取、修改、删除;
- 经典类中的属性只有一种访问方式,其对应被 @property 修饰的方法;
- 新式类中的属性有三种访问方式,并分别对应了三个被@property、@方法名.setter、@方法名.deleter修饰的方法;
#coding=utf-8
# ############### 定义 ###############
class Goods:
"""python3中默认继承object类
以python2、3执行此程序的结果不同,因为只有在python3中才有@xxx.setter @xxx.deleter
"""
@property
def price(self):
print('@property')
@price.setter
def price(self, value):
print('@price.setter')
@price.deleter
def price(self):
print('@price.deleter')
# ############### 调用 ###############
obj = Goods()
obj.price # 自动执行 @property 修饰的 price 方法,并获取方法的返回值 (获取值
obj.price = 123 # 自动执行 @price.setter 修饰的 price 方法,并将 123 赋值给方法的参数 (赋值
del obj.price # 自动执行 @price.deleter 修饰的 price 方法 (删除值
实例Demo:
class Goods(object):
def __init__(self):
# 原价
self.original_price = 100
# 折扣
self.discount = 0.8
@property
def price(self):
# 实际价格 = 原价 * 折扣
new_price = self.original_price * self.discount
return new_price
@price.setter
def price(self, value):
self.original_price = value
@price.deleter
def price(self):
del self.original_price
obj = Goods()
obj.price # 获取商品价格
obj.price = 200 # 修改商品原价
del obj.price # 删除商品原价
2.2.2. 类属性创建property属性
通俗的说,在类里面定义一个 类属性=property(方法名, ...) 。调用时,对象.类属性名 自动调用方法名对应的方法。
如下示例:
类属性BAR = property(get_bar),实例对象为obj,obj.BAR会自动调用给property传的get_bar方法,并获取方法的返回值。
class Foo:
def get_bar(self):
return 'laowang'
BAR = property(get_bar)
obj = Foo()
reuslt = obj.BAR # 自动调用get_bar方法,并获取方法的返回值
print(reuslt)
实际上,property方法中有四个参数 :( 用help(property)可查 )
- 参数1是方法名,接收要获取值时对应函数的函数引用,调用 对象.属性 时自动触发执行方法;
- 参数2是方法名,接收要设置值时对应函数的函数引用,调用 对象.属性 = XXX 时自动触发执行方法;
- 参数3是方法名,接收要删除值时对应函数的函数引用,调用 del 对象.属性 时自动触发执行方法;
- 参数4是字符串,调用 对象.属性.__doc__ ,此参数是该属性的描述信息。
由于类属性方式
创建property属性具有3种访问方式,我们可以根据它们几个属性的访问特点,分别将三个方法定义为对同一个属性:获取、修改、删除。
实例Demo:
#coding=utf-8
class Foo(object):
def get_bar(self):
print("getter...")
return 'laowang'
def set_bar(self, value):
"""必须两个参数"""
print("setter...")
return 'set value' + value
def del_bar(self):
print("deleter...")
return 'laowang'
BAR = property(get_bar, set_bar, del_bar, "description...")
obj = Foo()
obj.BAR # 自动调用第一个参数中定义的方法:get_bar
obj.BAR = "alex" # 自动调用第二个参数中定义的方法:set_bar方法,并将“alex”当作参数传入
desc = Foo.BAR.__doc__ # 自动获取第四个参数中设置的值:description...
print(desc)
del obj.BAR # 自动调用第三个参数中定义的方法:del_bar方法
综上:
- 定义property属性共有两种方式,分别是【装饰器】和【类属性】,而【装饰器】方式针对经典类和新式类又有所不同。
- 通过使用property属性,能够简化调用者在获取数据的流程。
注:WEB框架 Django 的视图中 request.POST 就是使用的类属性的方式创建的property属性。
2.3. property属性应用
2.3.1. 私有属性添加getter和setter方法(python不推荐这样做)
私有属性外部无法访问,添加getter和setter方法用来获取和设置。
如下:
class Money(object):
def __init__(self):
self.__money = 0
def getMoney(self):
return self.__money
def setMoney(self, value):
if isinstance(value, int): # 判断对象value是否是一个int类型
self.__money = value
else:
print("error:不是整型数字")
2.3.2. 使用property升级getter和setter方法(类属性方式)
用 property属性(类属性)来达到getter和setter的目的。好处,调用时就像操作一个属性(变量)一样,无需关心方法的代码。
如下:
class Money(object):
def __init__(self):
self.__money = 0
def getMoney(self):
return self.__money
def setMoney(self, value):
if isinstance(value, int):
self.__money = value
else:
print("error:不是整型数字")
# 定义一个属性,当对这个money设置值时调用setMoney,当获取值时调用getMoney
money = property(getMoney, setMoney)
a = Money()
a.money = 100 # 调用setMoney方法
print(a.money) # 调用getMoney方法
# 输出 100
2.3.3. 使用property取代getter和setter方法(装饰器方式)
用 property属性(装饰器)来达到getter和setter的目的。如下:
class Money(object):
def __init__(self):
self.__money = 0
# 使用装饰器对money进行装饰,那么会自动添加一个叫money的属性,当调用获取money的值时,调用装饰的方法
@property
def money(self):
return self.__money
# 使用装饰器对money进行装饰,当对money设置值时,调用装饰的方法
@money.setter
def money(self, value):
if isinstance(value, int):
self.__money = value
else:
print("error:不是整型数字")
a = Money()
a.money = 100
print(a.money)
3. 魔法属性/方法
引入:其实python的 私有属性 并非不可访问,而是通过名字重整(重整原则: _类名__属性名),使原来的名字无法访问了。如上例中, 通过 a.__dict__ 可返回类或对象中的所有属性(字典方式),包括私有属性;通过 对象._类名__属性名 即可访问属性,如 a._Money__money 即可访问私有属性money。
对于一个类,python定义了许多可用的魔法属性,存在着一些具有特殊含义的属性,有些每个类都默认存在,有些需要用户手动定义。常用的魔法属性如下。
3.1. __doc__
- 该属性表示类的描述信息,记录了类的说明文档,用 类 和 实例引用 指向的都是类的__doc__属性,如果没有默认为None。
class Foo:
""" 描述类信息、说明文档 """
def func(self):
pass
print(Foo.__doc__)
#输出:类的描述信息
3.2. __module__ 和 __class__
- __module__ 表示当前操作的对象在那个模块。该属性记录类定义的位置(哪个模块),如果定义的位置正好是主程序,那么该值为"_main_",否则是类属于的模块的名字;
- __class__ 表示当前操作的对象(实例)的类是什么。该属性指向该对象(实例)的类,即实例指向类对象,类对象指向元类;
test.py
# -*- coding:utf-8 -*-
class Person(object):
def __init__(self):
self.name = 'laowang'
main.py
from test import Person
obj = Person()
print(obj.__module__) # 输出 test 即:输出模块 (因为对象obj对应的类定义在模块test.py)
print(obj.__class__) # 输出 test.Person 即:输出类 (因为obj由test模块的Person创建)
3.3. __new__
该方法是类创建实例调用的第一个方法,返回一个实例;这是一个实例从无到有必须调用的方法,在单例模式中常用,其他不常用。创建实例时会将参数传入new方法,但new方法中无法更改参数。
class Person(object):
def __new__(cls, *args, **kwargs):
print(args)
return object.__new__(cls)
if __name__ == "__main__":
person = Person('cai')
3.4. __init__
- 初始化方法,通过类创建对象时,自动触发执行。python在调用new方法后会紧接着调用init方法,我们将实例的一些初始化操作放在该方法中,即对__dict__属性进行操作。(__new__方法和__init__方法加起来才类似其他语言的构造方法)
class Person:
def __init__(self, name):
self.name = name
self.age = 18
obj = Person('laowang') # 自动执行类中的 __init__ 方法
3.5. __del__
- 当对象在内存中被释放时,自动触发执行。即在实例对象引用计数变为0或del关键字调用的时候触发执行。
注:此方法一般无须定义,因为Python是一门高级语言,程序员在使用时无需关心内存的分配和释放,因为此工作都是交给Python解释器来执行,所以,__del__的调用是由解释器在进行垃圾回收时自动触发执行的。
class Foo:
def __del__(self):
pass
3.6. __call__
- 对象后面加括号,触发执行。
注:
- __init__方法的执行是由创建对象触发的,即:
对象 = 类名()
; - 而对于 __call__ 方法的执行是由对象后加括号触发的,即:
对象()
或者类()()
(注:类名()创建了对象,再加一个括号等价与对象())
class Foo:
def __init__(self):
pass
def __call__(self, *args, **kwargs): # 保证有call方法
print('__call__')
obj = Foo() # 执行 __init__
obj() # 执行 __call__
3.7. __dict__
返回类或对象中的所有属性。
又分为 类的__dict__属性 和 实例的__dict__属性。类的实例属性属于对象;类中的类属性和方法等属于类。
-
类的__dict__属性: 存储了类定义的所有类属性、类方法等组成的键值对,但不包括继承而来的属性和方法;
-
实例的__dict__属性: 存储了所有的实例属性的键值对,如果没有就为空;
-
__init__方法其实就是对__dict__属性的初始化赋值;
class Province(object):
country = 'China'
def __init__(self, name, count):
self.name = name
self.count = count
def func(self, *args, **kwargs):
print('func')
# 获取类的属性,即:类属性、方法、
print(Province.__dict__)
# 输出:{'__dict__': <attribute '__dict__' of 'Province' objects>, '__module__': '__main__', 'country': 'China', '__doc__': None, '__weakref__': <attribute '__weakref__' of 'Province' objects>, 'func': <function Province.func at 0x101897950>, '__init__': <function Province.__init__ at 0x1018978c8>}
obj1 = Province('山东', 10000)
print(obj1.__dict__)
# 获取 对象obj1 的属性
# 输出:{'count': 10000, 'name': '山东'}
obj2 = Province('山西', 20000)
print(obj2.__dict__)
# 获取 对象obj1 的属性
# 输出:{'count': 20000, 'name': '山西'}
3.8. __str__
- 如果一个类中定义了__str__方法,那么在打印 对象 时,默认输出该方法的返回值。
class Foo:
def __str__(self):
return 'hello'
obj = Foo()
print(obj)
# 输出:hello
3.9. __getitem__、__setitem__、__delitem__
- 用于索引操作,如字典。以上分别表示获取、设置、删除数据。
下例,obj['k1'] 把 obj 对象当成了字典对象。
# -*- coding:utf-8 -*-
class Foo(object):
def __getitem__(self, key):
print('__getitem__', key)
def __setitem__(self, key, value):
print('__setitem__', key, value)
def __delitem__(self, key):
print('__delitem__', key)
obj = Foo()
result = obj['k1'] # 自动触发执行 __getitem__, 把k1 传给了__getitem__的参数 key
obj['k2'] = 'hello' # 自动触发执行 __setitem__, k2、'hello' 分别传给 __setitem__ 的参数 key、value
del obj['k1'] # 自动触发执行 __delitem__
3.10. __getslice__、__setslice__、__delslice__
- 该三个方法用于分片操作,如:列表
# -*- coding:utf-8 -*-
class Foo(object):
def __getslice__(self, i, j):
print('__getslice__', i, j)
def __setslice__(self, i, j, sequence):
print('__setslice__', i, j)
def __delslice__(self, i, j):
print('__delslice__', i, j)
obj = Foo()
obj[-1:1] # 自动触发执行 __getslice__(切片)
obj[0:1] = [11,22,33,44] # 自动触发执行 __setslice__(注:给列表切片设置值)
del obj[0:2] # 自动触发执行 __delslice__
4.面向对象设计
三大编程范式(编程范式即编程的方法论,一种编程风格):
1.面向过程编程
2.函数式编程
3.面向对象编程
面向对象:
- 继承 - 是基于Python中的属性查找(如X.name)
- 多态 - 在X.method方法中,method的意义取决于X的类型
- 封装 - 方法和运算符实现行为,数据隐藏默认是一种惯例
以下实例主要用于理解面向对象的设计思想。
参考实例1:腾讯即时通信模块的初级封装。
- 定义了类 Message ;
- 定义了类 MsgDict 作为基类,其他消息类型继承自该类;
#! /usr/bin/env python
# coding: utf-8
import random
import time
class Message(object):
def __init__(self, msgarr=[], toacc=''):
self.msgbody = msgarr # 此处为MsgDict对象实例的列表或者空列表
self.toacc = toacc # toacc为字符串(单发)或者列表(批量发)
self.msgrandom = random.randint(1, 1000000000)
self.msgrequest = {
'To_Account': toacc, # 消息接收方账号
'MsgRandom': self.msgrandom, # 消息随机数,由随机函数产生
'MsgBody': [t.msg for t in msgarr]
}
def del_option(self, option):
if option in (set(self.msgrequest)-set(['To_Account', 'MsgRandom', 'MsgBody'])):
self.__dict__.pop(option)
self.msgrequest.pop(option)
def append_msg(self, msg):
self.msgbody.append(msg)
self.msgrequest['MsgBody'].append(msg.msg)
def insert_msg(self, index, msg):
self.msgbody.insert(index, msg)
self.msgrequest['MsgBody'].insert(msg.msg)
def del_msg(self, index):
if index in range(len(self.msgbody)):
del self.msgbody[index]
del sel.msgrequest['MsgBody'][index]
def set_from(self, fromacc):
# 指定消息的发送方,默认为服务器发送
self.fromacc = fromacc
self.msgrequest['From_Account'] = fromacc
def set_to(self, toacc):
# 指定消息的接收方,可以为String(单发),可以为List(批量发送)
self.toacc = toacc
self.msgrequest['To_Account'] = toacc
def refresh_random(self):
self.msgrandom = random.randint(1, 1000000000)
self.msgrequest['MsgRandom'] = self.msgrandom, # 消息随机数,由随机函数产生
def set_sync(self, sync):
# 同步选项:1, 把消息同步到From_Account在线终端和漫游上
# 2, 消息不同步至From_Account
# 若不填写,默认情况下会将消息同步
# 仅在单发单聊消息中可调用
self.sync = sync
self.msgrequest['SyncOtherMachine'] = sync
def set_timestamp(self):
# 设置消息时间戳,unix时间, 仅在单发单聊消息中可以调用
self.timestamp = int(time.time())
self.msgrequest['MsgTimeStamp'] = self.timestamp
def set_offlinepush(self, pushflag=0, desc='', ext='', sound=''):
# 仅适用于APNa推送,不适用于安卓厂商推送
self.msgrequest['OfflinePushInfo'] = {
'PushFlag': pushflag,
'Desc': desc,
'Ext': ext,
'Sound': sound
}
class MsgDict(object):
def __init__(self, msgtype='', msgcontent={}):
self.msgtype = msgtype
self.msgcontent = msgcontent
@property
def msg(self):
return {
'MsgType': self.msgtype,
'MsgContent': self.msgcontent
}
def set_content(self, content):
self.msgcontent = content
class TextMsg(MsgDict):
def __init__(self, text='', msgtype='TIMTextElem'):
self.text = text
content = {'Text': text}
super(TextMsg, self).__init__(msgtype=msgtype, msgcontent=content)
def set_text(self, text):
self.text = text
self.msgcontent['Text'] = text
class LocationMsg(MsgDict):
def __init__(self, desc='', latitude=0, longitude=0, msgtype='TIMLocationElem'):
self.desc = desc
self.latitude = latitude
self.longitude = longitude
content = {
'Desc': desc, # 地理位置描述信息, String
'Latitude': latitude, # 纬度, Number
'Longitude': longitude # 经度, Number
}
super(LocationMsg, self).__init__(msgtype=msgtype, msgcontent=content)
def set_desc(self, desc):
self.desc = desc
self.msgcontent['Desc'] = desc
def set_location(self, latitude, longitude):
self.latitude = latitude
self.longitude = longitude
self.msgcontent['Latitude'] = latitude
self.msgcontent['Longitude'] = longitude
def set_latitude(self, latitude):
self.latitude = latitude
self.msgcontent['Latitude'] = latitude
def set_longitude(self, longitude):
self.longitude = longitude
self.msgcontent['Longitude'] = longitude
class FaceMsg(MsgDict):
def __init__(self, index=1, data='', msgtype='TIMFaceElem'):
self.index = index
self.data = data
content = {
'Index': index, # 表情索引,用户自定义, Number
'Data': data # 额外数据, String
}
super(TextMsg, self).__init__(msgtype=msgtype, msgcontent=content)
def set_index(self, index):
self.index = index
self.msgcontent['Index'] = index
def set_data(self, data):
self.data = data
self.msgcontent['Data'] = data
class CustomMsg(MsgDict):
def __init__(self, data='', desc='', ext='', sound='', msgtype='TIMCustomElem'):
self.data = data
self.desc = desc
self.ext = ext
self.sound = sound
content = {
'Data': data, # 自定义消息数据。不作为APNS的payload中字段下发,故从payload中无法获取Data字段, String
'Desc': desc, # 自定义消息描述,当接收方为iphone后台在线时,做ios离线Push时文本展示
'Ext': ext, # 扩展字段,当接收方为ios系统且应用处在后台时,此字段作为APNS请求包Payloads中的ext键值下发,Ext的协议格式由业务方确定,APNS只做透传
'Sound': sound # 自定义APNS推送铃声
}
super(CustomMsg, self).__init__(msgtype=msgtype, msgcontent=content)
def set_data(self, data):
self.data = data
self.msgcontent['Data'] = data
def set_desc(self, desc):
self.desc = desc
self.msgcontent['Desc'] = desc
def set_ext(self, ext):
self.ext = ext
self.msgcontent['Ext'] = ext
def set_sound(self, sound):
self.sound = sound
self.msgcontent['Sound'] = sound
class SoundMsg(MsgDict):
def __init__(self, uuid='', size=0, second=0, msgtype='TIMSoundElem'):
self.uuid = uuid
self.size = size
self.second = second
content = {
'UUID': uuid, # 语音序列号,后台用于索引语音的键值,String
'Size': size, # 语音数据大小, Number
'Second': second # 语音时长,单位秒 Number
}
super(SoundMsg, self).__init__(msgtype=msgtype, msgcontent=content)
def set_uuid(self, uuid):
self.uuid = uuid
self.msgcontent['UUID'] = uuid
def set_size(self, size):
self.size = size
self.msgcontent['Size'] = size
def set_second(self, second):
self.second = second
self.msgcontent['Second'] = second
class ImageMsg(MsgDict):
def __init__(self, uuid='', imgformat=0, imginfo=[], msgtype='TIMImageElem'):
self.uuid = uuid
self.imgformat = imgformat
self.imginfo = imginfo
content = {
'UUID': uuid, # 图片序列号,后台用于索引语音的键值,String
'ImageFormat': imgformat, # 图片格式, BMP=1, JPG=2, GIF=3, 其他=0, Number
'ImageInfoArray': [t.info for t in imginfo] # 原图,缩略图或者大图下载信息, Array
}
super(ImageMsg, self).__init__(msgtype=msgtype, msgcontent=content)
def set_uuid(self, uuid):
self.uuid = uuid
self.msgcontent['UUID'] = uuid
def set_format(self, imgformat):
self.imgformat = imgformat
self.msgcontent['ImageFormat'] = imgformat
def append_info(self, info):
# info 为ImageInfo对象实例
self.imginfo.append(info)
self.msgcontnet['ImageInfoArray'].append(info.info)
def insert_info(self, index, info):
self.imginfo.insert(index, info)
self.msgcontent['ImageInfoArray'].insert(index, info.info)
def del_info(self, index):
del self.imginfo[index]
del self.msgcontent['ImageInfoArray'][index]
class FileMsg(MsgDict):
def __init__(self, uuid='', size=0, name='', msgtype='TIMFileElem'):
self.uuid = uuid
self.size = size
self.name = name
content = {
'UUID': uuid, # 文件序列号,后台用于索引语音的键值,String
'FileSize': size, # 文件数据大小, Number
'FileName': name # 文件名称/路径, String
}
super(FileMsg, self).__init__(msgtype=msgtype, msgcontent=content)
def set_uuid(self, uuid):
self.uuid = uuid
self.msgcontent['UUID'] = UUID
def set_size(self, size):
self.size = size
self.msgcontent['FileSize'] = size
def set_name(self, name):
self.name = name
self.msgcontent['FileName'] = name
class ImageInfo(object):
def __init__(self, itype=1, size=0, width=0, height=0, url=''):
#图片类型, 1-原图, 2-大图, 3-缩略图, 0-其他
self.itype = itype
# 图片数据大小,Number
self.size = size
# 图片宽度,Number
self.width = width
# 图片高度, Number
self.height = height
# 图片下载地址,String
self.url = url
@property
def info(self):
return {
'Type': self.itype,
'Size': self.size,
'Width': self.width,
'Height': self.height,
'URL': self.url
}
def set_type(self, itype):
self.itype = itype
def set_size(self, size):
self.size = size
def set_width(self, width):
self.width = width
def set_height(self, height):
self.height = height
def set_url(self, url):
self.url = url
5.with与 上下文管理器
对于系统资源如文件、数据库连接、socket 而言,应用程序打开这些资源并执行完业务逻辑之后,必须做的一件事就是要关闭(断开)该资源。
比如 Python 程序打开一个文件,往文件中写内容,写完之后,就要关闭该文件。否则可能出现 "Too many open files"等错误。
对于数据库,如果连接数过多而没有及时关闭的话,就可能会出现 "Can not connect to MySQL server Too many connections"。
5.1 with关键字
以下三个例子演示了正确关闭一个文件:
例子1 普通版:
def m1(): f = open("output.txt", "w") f.write("hello python") f.close()
这样写有一个潜在的问题,如果在调用 write 的过程中,出现了异常进而导致后续代码无法继续执行,close 方法无法被正常调用,因此资源就会一直被该程序占用者释放。
例子2 进阶版:
def m2(): f = open("output.txt", "w") try: f.write("hello python") except IOError: print("oops error") finally: # 一定会执行finally f.close()
对可能发生异常的代码处进行 try 捕获,使用 try/finally 语句,该语句表示如果在 try 代码块中程序出现了异常,后续代码就不再执行,而直接跳转到 except 代码块。而无论如何,finally 块的代码最终都会被执行,文件一定会被关闭。缺点是增加了许多代码。
例子3 with版本:
def m3(): with open("output.txt", "r") as f: f.write("hello Python")
用 with 关键字是一种更加简洁、优雅的方式。open 方法的返回值赋值给变量 f,当离开 with 代码块的时候(无论是否出现异常),系统会自动调用 f.close() 方法, with 的作用和使用 try/finally 语句是一样的。
那么它的实现原理是什么?在讲 with 的原理前要涉及到另外一个概念,就是上下文管理器(Context Manager)。
5.2. 上下文管理器(Context Manager)
任何实现了 __enter__() 方法 和 __exit__() 方法的对象都可称之为上下文管理器,上下文管理器对象可以使用 with 关键字。显然,文件(file)对象也实现了上下文管理器。
如,以下定义了一个包含 __enter__() 方法 和 __exit__() 方法 的类 File:
class File():
def __init__(self, filename, mode):
self.filename = filename
self.mode = mode
def __enter__(self):
print("entering")
self.f = open(self.filename, self.mode)
return self.f
def __exit__(self, *args):
print("will exit")
self.f.close()
__enter__() 方法返回资源对象,这里就是你将要打开的那个文件对象,__exit__() 方法处理一些清除工作。
因为 File 类实现了上下文管理器,可以使用 with 语句,如下。
with File('out.txt', 'w') as f:File('out.txt', 'w') 返回对象的引用,with 检查其是否是上下文管理器,若是则其会自动调用对象的__enter__() 方法,__enter__()的返回值给 f 。若产生了异常,会调用对象的__exit__() 方法。
with File('out.txt', 'w') as f:
print("writing")
f.write('hello, python')
启发:UDP和TCP的Demo中,将其套接字、和使用套接字的函数封装成类,并实现 __enter__() 方法 和 __exit__()方法 。这样收发消息时,无论是否产生异常,都会自动调用套接字的close。
实现上下文管理器的另外方式:
Python 还提供了一个 contextmanager 的装饰器,更进一步简化了上下文管理器的实现方式。通过 yield 将函数分割成两部分,yield 之前的语句在 __enter__ 方法中执行,yield 之后的语句在 __exit__ 方法中执行。 yield 后面的值是函数的返回值。
如下: f = open(path, mode) 语句在 __enter__ 方法中执行,yield 之后的语句 f.close() 在 __exit__ 方法中执行,my_open方法返回 f 。
from contextlib import contextmanager
@contextmanager
def my_open(path, mode):
f = open(path, mode)
yield f # f 为方法的返回值
f.close()
# 调用
with my_open('out.txt', 'w') as f:
f.write("hello , the simplest context manager")
with/上下文管理器小结:
Python 提供了 with 语法用于简化资源操作的后续清除操作,是 try/finally 的替代方法,实现原理建立在上下文管理器之上。此外,Python 还提供了一个 contextmanager 装饰器,更进一步简化上下管理器的实现方式。
----------实例来源于课件、网络等--------
-----------END-----------