1. 浅拷贝、深拷贝
1.1 浅拷贝
-
浅拷贝是对于一个对象的顶层拷贝 -
通俗的理解是:拷贝了引用,并没有拷贝内容
1.2 深拷贝
-
深拷贝是对于一个对象所有层次的拷贝(递归)
1.3 注意点
-
浅拷贝对不可变类型和可变类型的copy不同 -
copy.copy对于可变类型,会进行浅拷贝 -
copy.copy对于不可变类型,不会拷贝,仅仅是指向
2. 私有化
-
xx
: 公有变量 -
_x
: 单前置下划线,私有化属性或方法,from somemodule import *禁止导入,类对象和子类可以访问 -
__xx
:双前置下划线,避免与子类中的属性命名冲突,无法在外部直接访问(名字重整所以访问不到) -
__xx__
:双前后下划线,用户名字空间的魔法对象或属性。例如:__init__
,__
不要自己发明这样的名字 -
xx_
:单后置下划线,用于避免与Python关键词的冲突 -
通过name mangling(名字重整(目的就是以防子类意外重写基类的方法或者属性)如:_Class__object)机制就可以访问private了 -
注意
父类中属性名为 __
名字的,子类不继承,子类不能访问如果在子类中向 __名字
赋值,那么会在子类中定义的一个与父类相同名字的属性_名
的变量、函数、类在使用from xxx import *
时都不会被导入
#coding=utf-8
class Person(object):
def __init__(self, name, age, taste):
self.name = name
self._age = age
self.__taste = taste
def showperson(self):
print(self.name)
print(self._age)
print(self.__taste)
def dowork(self):
self._work()
self.__away()
def _work(self):
print('my _work')
def __away(self):
print('my __away')
class Student(Person):
def construction(self, name, age, taste):
self.name = name
self._age = age
self.__taste = taste
def showstudent(self):
print(self.name)
print(self._age)
print(self.__taste)
@staticmethod
def testbug():
_Bug.showbug()
# 模块内可以访问,当from cur_module import *时,不导入
class _Bug(object):
@staticmethod
def showbug():
print("showbug")
s1 = Student('jack', 25, 'football')
s1.showperson()
print('*'*20)
# 无法访问__taste,导致报错
# s1.showstudent()
s1.construction('rose', 30, 'basketball')
s1.showperson()
print('*'*20)
s1.showstudent()
print('*'*20)
Student.testbug()
3. import导入模块
3.1 import 搜索路径
从上面列出的目录里依次查找要导入的模块文件 ''
表示当前路径列表中的路径的先后顺序代表了python解释器在搜索模块时的先后顺序
程序执行时添加新的模块路径
sys.path.append('/home/itcast/xxx') # 追加
sys.path.insert(0, '/home/itcast/xxx') # 指定位置新增,可以确保先搜索这个路径
搜索路径如下
['/home/python/xxxx',
'',
'/usr/bin',
'/usr/lib/python35.zip',
'/usr/lib/python3.5',
'/usr/lib/python3.5/plat-x86_64-linux-gnu',
'/usr/lib/python3.5/lib-dynload',
'/usr/local/lib/python3.5/dist-packages',
'/usr/lib/python3/dist-packages',
'/usr/lib/python3/dist-packages/IPython/extensions',
'/home/python/.ipython']
3.2 重新导入模块
模块被导入后,import module
不能重新导入模块,重新导入需用reload
4. 多继承以及MRO顺序
4.1 总结
-
super().__init__
相对于类名.__init__
,在单继承上用法基本无差 -
但在多继承上有区别
super
方法能保证每个父类的方法只会执行一次而使用类名的方法会导致方法被执行多次
-
多继承时,使用 super
方法,对父类的传参数,应该是由于python中super的算法导致的原因,必须把参数全部传递,否则会报错 -
单继承时,使用 super
方法,则不能全部传递,只能传父类方法所需的参数,否则会报错 -
多继承时,相对于使用 类名.__init__
方法,要把每个父类全部写一遍,而使用super方法,只需写一句话便执行了全部父类的方法,这也是为何多继承需要全部传参的一个原因
4.2 单独调用父类的方法
# coding=utf-8
print("******多继承使用类名.__init__ 发生的状态******")
class Parent(object):
def __init__(self, name):
print('parent的init开始被调用')
self.name = name
print('parent的init结束被调用')
class Son1(Parent):
def __init__(self, name, age):
print('Son1的init开始被调用')
self.age = age
Parent.__init__(self, name)
print('Son1的init结束被调用')
class Son2(Parent):
def __init__(self, name, gender):
print('Son2的init开始被调用')
self.gender = gender
Parent.__init__(self, name)
print('Son2的init结束被调用')
class Grandson(Son1, Son2):
def __init__(self, name, age, gender):
print('Grandson的init开始被调用')
Son1.__init__(self, name, age) # 单独调用父类的初始化方法
Son2.__init__(self, name, gender)
print('Grandson的init结束被调用')
gs = Grandson('grandson', 12, '男')
print('姓名:', gs.name)
print('年龄:', gs.age)
print('性别:', gs.gender)
print("******多继承使用类名.__init__ 发生的状态******nn")
-
运行结果如下
******多继承使用类名.__init__ 发生的状态******
Grandson的init开始被调用
Son1的init开始被调用
parent的init开始被调用
parent的init结束被调用
Son1的init结束被调用
Son2的init开始被调用
parent的init开始被调用
parent的init结束被调用
Son2的init结束被调用
Grandson的init结束被调用
姓名: grandson
年龄: 12
性别: 男
******多继承使用类名.__init__ 发生的状态******
4.3 多继承中super调用有所父类的被重写的方法
-
如果2个子类中都继承了父类,当在子类中通过父类名调用时,parent被执行了2次 -
如果2个子类中都继承了父类,当在子类中通过super调用时,parent被执行了1次
print("******多继承使用super().__init__ 发生的状态******")
class Parent(object):
def __init__(self, name, *args, **kwargs): # 为避免多继承报错,使用不定长参数,接受参数
print('parent的init开始被调用')
self.name = name
print('parent的init结束被调用')
class Son1(Parent):
def __init__(self, name, age, *args, **kwargs): # 为避免多继承报错,使用不定长参数,接受参数
print('Son1的init开始被调用')
self.age = age
super().__init__(name, *args, **kwargs) # 为避免多继承报错,使用不定长参数,接受参数
print('Son1的init结束被调用')
class Son2(Parent):
def __init__(self, name, gender, *args, **kwargs): # 为避免多继承报错,使用不定长参数,接受参数
print('Son2的init开始被调用')
self.gender = gender
super().__init__(name, *args, **kwargs) # 为避免多继承报错,使用不定长参数,接受参数
print('Son2的init结束被调用')
class Grandson(Son1, Son2):
def __init__(self, name, age, gender):
print('Grandson的init开始被调用')
# 多继承时,相对于使用类名.__init__方法,要把每个父类全部写一遍
# 而super只用一句话,执行了全部父类的方法,这也是为何多继承需要全部传参的一个原因
# super(Grandson, self).__init__(name, age, gender)
super().__init__(name, age, gender)
print('Grandson的init结束被调用')
print(Grandson.__mro__)
gs = Grandson('grandson', 12, '男')
print('姓名:', gs.name)
print('年龄:', gs.age)
print('性别:', gs.gender)
print("******多继承使用super().__init__ 发生的状态******nn")
-
运行结果:
******多继承使用super().__init__ 发生的状态******
(<class '__main__.Grandson'>, <class '__main__.Son1'>, <class '__main__.Son2'>, <class '__main__.Parent'>, <class 'object'>)
Grandson的init开始被调用
Son1的init开始被调用
Son2的init开始被调用
parent的init开始被调用
parent的init结束被调用
Son2的init结束被调用
Son1的init结束被调用
Grandson的init结束被调用
姓名: grandson
年龄: 12
性别: 男
******多继承使用super().__init__ 发生的状态******
4.4 单继承中super
print("******单继承使用super().__init__ 发生的状态******")
class Parent(object):
def __init__(self, name):
print('parent的init开始被调用')
self.name = name
print('parent的init结束被调用')
class Son1(Parent):
def __init__(self, name, age):
print('Son1的init开始被调用')
self.age = age
super().__init__(name) # 单继承不能提供全部参数
print('Son1的init结束被调用')
class Grandson(Son1):
def __init__(self, name, age, gender):
print('Grandson的init开始被调用')
super().__init__(name, age) # 单继承不能提供全部参数
print('Grandson的init结束被调用')
gs = Grandson('grandson', 12, '男')
print('姓名:', gs.name)
print('年龄:', gs.age)
#print('性别:', gs.gender)
print("******单继承使用super().__init__ 发生的状态******nn")
5. property属性
-
一种用起来像是使用的实例属性一样的特殊属性,可以对应于某个方法 -
把方法,像属性那样使用 -
定义时,在实例方法的基础上添加 **@property **** 装饰器 ;并且仅有一个self参数** -
调用时,无需括号
方法:foo_obj.func() property属性:foo_obj.prop
-
property属性的功能是:property属性内部进行一系列的逻辑计算,最终将计算结果返回
# ############### 定义 ###############
class Foo:
def func(self):
pass
# 定义property属性
@property
def prop(self):
pass
# ############### 调用 ###############
foo_obj = Foo()
foo_obj.func() # 调用实例方法
foo_obj.prop # 调用property属性
5.1 property属性的有两种方式
-
装饰器 即:在方法上应用装饰器 -
类属性 即:在类中定义值为property对象的类属性
装饰器方式
-
在类的实例方法上应用@property装饰器 -
Python中的类有经典类和新式类,新式类的属性比经典类的属性丰富。(如果类继object,那么该类是新式类)
经典类,具有一种@property装饰器
-
经典类中的属性只有一种访问方式,其对应被 @property 修饰的方法
# ############### 定义 ###############
class Goods:
@property
def price(self):
return "laowang"
# ############### 调用 ###############
obj = Goods()
result = obj.price # 自动执行 @property 修饰的 price 方法,并获取方法的返回值
print(result)
新式类,具有三种@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 方法
-
由于新式类中具有三种访问方式,我们可以根据它们几个属性的访问特点,分别将三个方法定义为对同一个属性:获取、修改、删除
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 # 删除商品原价
类属性方式,创建值为property对象的类属性
-
当使用类属性的方式创建property属性时, 经典类
和新式类
无区别
class Foo:
def get_bar(self):
return 'laowang'
BAR = property(get_bar)
obj = Foo()
reuslt = obj.BAR # 自动调用get_bar方法,并获取方法的返回值
print(reuslt)
-
property方法中有个四个参数
第一个参数是方法名,调用 对象.属性
时自动触发执行方法第二个参数是方法名,调用 对象.属性 = XXX
时自动触发执行方法第三个参数是方法名,调用 del 对象.属性
时自动触发执行方法第四个参数是字符串,调用 对象.属性.__doc__
,此参数是该属性的描述信息
#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属性具有3种访问方式,我们可以根据它们几个属性的访问特点,分别将三个方法定义为对同一个属性:获取、修改、删除
class Goods(object):
def __init__(self):
# 原价
self.original_price = 100
# 折扣
self.discount = 0.8
def get_price(self):
# 实际价格 = 原价 * 折扣
new_price = self.original_price * self.discount
return new_price
def set_price(self, value):
self.original_price = value
def del_price(self):
del self.original_price
PRICE = property(get_price, set_price, del_price, '价格属性描述...')
obj = Goods()
obj.PRICE # 获取商品价格
obj.PRICE = 200 # 修改商品原价
del obj.PRICE # 删除商品原价
5.2 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:不是整型数字")
使用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
使用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)
6. 魔法属性
6.1__doc__
-
表示类的描述信息
class Foo:
""" 描述类信息,这是用于看片的神奇 """
def func(self):
pass
print(Foo.__doc__)
#输出:类的描述信息
6.2 __module__
和 __class__
-
__module__
表示当前操作的对象在那个模块 -
__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 即:输出模块
print(obj.__class__) # 输出 test.Person 即:输出类
6.3__init__
-
初始化方法,通过类创建对象时,自动触发执行
class Person:
def __init__(self, name):
self.name = name
self.age = 18
obj = Person('laowang') # 自动执行类中的 __init__ 方法
6.4__del__
-
当对象在内存中被释放时,自动触发执行。 -
注:此方法一般无须定义,因为Python是一门高级语言,程序员在使用时无需关心内存的分配和释放,因为此工作都是交给Python解释器来执行,所以,del的调用是由解释器在进行垃圾回收时自动触发执行的。
class Foo:
def __del__(self):
pass
6.5__call__
-
对象后面加括号,触发执行。 -
注: __init__
方法的执行是由创建对象触发的,即:对象 = 类名()
; -
而对于 __call__
方法的执行是由对象后加括号触发的,即:对象()
或者类()()
class Foo:
def __init__(self):
pass
def __call__(self, *args, **kwargs):
print('__call__')
obj = Foo() # 执行 __init__
obj() # 执行 __call__
6.6 __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': '山西'}
6.7 __str__
-
如果一个类中定义了 __str__
方法,那么在打印 对象 时,默认输出该方法的返回值。
class Foo:
def __str__(self):
return 'laowang'
obj = Foo()
print(obj)
# 输出:laowang
6.8 __getitem__
、__setitem__
、__delitem__
-
用于索引操作,如字典。以上分别表示获取、设置、删除数据
# -*- 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__
obj['k2'] = 'laowang' # 自动触发执行 __setitem__
del obj['k1'] # 自动触发执行 __delitem__
6.9 __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__
7. with与上下文管理器
7.1 with
-
普通版
def m1():
f = open("output.txt", "w")
f.write("python之禅")
f.close()
-
进阶版
def m2():
f = open("output.txt", "w")
try:
f.write("python之禅")
except IOError:
print("oops error")
finally:
f.close()
-
高级版
def m3():
with open("output.txt", "r") as f:
f.write("Python之禅")
-
一种更加简洁、优雅的方式就是用 with 关键字。 -
open 方法的返回值赋值给变量 f,当离开 with 代码块的时候,系统会自动调用 f.close() 方法, with 的作用和使用 try/finally 语句是一样的.
7.2 上下文管理器
-
任何实现了 __enter__()
和__exit__()
方法的对象都可称之为上下文管理器 -
上下文管理器对象可以使用 with 关键字。显然,文件(file)对象也实现了上下文管理器。
模拟实现一个自己的文件类,让该类实现__enter__()
和 __exit__()
方法
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:
print("writing")
f.write('hello, python')
实现上下文管理器的另外方式
-
Python 还提供了一个 contextmanager
的装饰器,更进一步简化了上下文管理器的实现方式。 -
通过 yield
将函数分割成两部分,yield
之前的语句在__enter__
方法中执行,yield 之后的语句在__exit__
方法中执行。紧跟在 yield 后面的值是函数的返回值。 -
定义一个具有上下文的类
from contextlib import contextmanager
@contextmanager
def my_open(path, mode):
f = open(path, mode)
yield f
f.close()
-
调用
with my_open('out.txt', 'w') as f:
f.write("hello , the simplest context manager")
8. 闭包
8.1 闭包是一个函数
-
为什么使用闭包 -
由于函数作用域的限制,在函数外,无法使用函数内的变量。 -
如果想使用函数内的变量,可以通过闭包来实现。 -
闭包就是,在一个函数A内创建一个函数B,函数B操作函数A内的变量,返回函数B。当调用函数A的时候,会返回函数B,此时返回值是一个函数。然后再次按照函数方式调用返回值即可。 -
注意:由于闭包引用了外部函数的局部变量,则外部函数的局部变量没有及时释放,消耗内存 -
定义:在函数嵌套的前提下,内部函数使用了外部函数的变量,并且外部函数返回了内部函数,我们把这个使用外部函数变量的内部函数称为闭包
8.2 闭包的构成条件
-
通过闭包的定义,我们可以得知闭包的形成条件:
在函数嵌套(函数里面再定义函数)的前提下 内部函数使用了外部函数的变量(还包括外部函数的参数) 外部函数返回了内部函数
简单闭包的示例代码
# 定义一个外部函数
def func_out(num1):
# 定义一个内部函数
def func_inner(num2):
# 内部函数使用了外部函数的变量(num1)
result = num1 + num2
print("结果是:", result)
# 外部函数返回了内部函数,这里返回的内部函数就是闭包
return func_inner
# 创建闭包实例
f = func_out(1)
# 执行闭包
f(2)
f(3)
8.3 闭包的作用
闭包可以保存外部函数内的变量,不会随着外部函数调用完而销毁。 注意点: 由于闭包引用了外部函数的变量,则外部函数的变量没有及时释放,消耗内存
8.4 小结
当返回的内部函数使用了外部函数的变量就形成了闭包 闭包可以对外部函数的变量进行保存
-
实现闭包的标准格式
# 外部函数
def test1(a):
b = 10
# 内部函数
def test2():
# 内部函数使用了外部函数的变量或者参数
print(a, b)
# 返回内部函数, 这里返回的内部函数就是闭包实例
return test2
8.5 修改闭包内使用的外部变量
-
修改闭包内使用的外部函数变量使用 nonlocal 关键字来完成
# 定义一个外部函数
def func_out(num1):
# 定义一个内部函数
def func_inner(num2):
# 这里本意想要修改外部num1的值,实际上是在内部函数定义了一个局部变量num1
nonlocal num1 # 告诉解释器,此处使用的是 外部变量a
# 修改外部变量num1
num1 = 10
# 内部函数使用了外部函数的变量(num1)
result = num1 + num2
print("结果是:", result)
print(num1)
func_inner(1)
print(num1)
# 外部函数返回了内部函数,这里返回的内部函数就是闭包
return func_inner
# 创建闭包实例
f = func_out(1)
# 执行闭包
f(2)
9. 装饰器
9.1 理解
-
装饰器(Decorators)是Python的一个重要部分。 -
装饰器是修改其他函数的功能的函数。 -
装饰器其实也就是一个函数,一个用来包装函数的函数,返回一个修改之后的函数对象,将其重新赋值原来的标识符,并永久丧失对原始函数对象的访问。 -
装饰器,当python从上向下解析的时候,会执行这个装饰器的
也就是说,当python从上向下解析文件的时候,哪怕不调用被装饰的函数,但是也会执行这个装饰器的 装饰器,不是调用被装饰的函数的是调用,而是解析的时候就调用这个装饰器
一切皆对象
-
Python中的函数,也是对象; -
可以修改函数这个对象的指向,这个是装饰器的基础
#### 第一波 ####
def foo():
print('foo')
foo # 表示是函数
foo() # 表示执行foo函数
#### 第二波 ####
def foo():
print('foo')
foo = lambda x: x + 1
foo() # 执行lambda表达式,而不再是原来的foo函数,因为foo这个名字被重新指向了另外一个匿名函数
-
函数名仅仅是个变量,只不过指向了定义的函数而已,所以才能通过 函数名()调用 -
如果 函数名=xxx被修改了,那么当在执行 **函数名()**时,调用的就不知之前的那个函数了
9.2 在函数中定义函数
-
在函数外无法调用函数内的函数
#!/usr/bin/env python
#coding:utf-8
def hi(name="yasoob"):
print("now you are inside the hi() function")
def greet():
return"now you are in the greet() function"
def welcome():
return"now you are in the welcome() function"
print(greet())
print(welcome())
print("now you are back in the hi() function")
hi()
#output:now you are inside the hi() function
# now you are in the greet() function
# now you are in the welcome() function
# now you are back in the hi() function
# 上面展示了无论何时你调用hi(), greet()和welcome()将会同时被调用。
# 然后greet()和welcome()函数在hi()函数之外是不能访问的,比如:
greet()# 在hi函数外无法访问hi函数内的函数
#outputs: NameError: name 'greet' is not defined
9.3 从函数中返回函数
-
闭包
#!/usr/bin/env python
#coding:utf-8
def hi(name="yasoob"):
def greet():
return"now you are in the greet() function"
def welcome():
return"now you are in the welcome() function"
if name =="yasoob":
return greet # 这里没有加(),是因为这里是赋值,而不是去执行
else:
return welcome
a = hi() #此时,a就是hi()函数的执行结果,因为hi后面有括号,代表执行hi()函数
print(a)
#outputs: <function greet at 0x7f2143c01500>
#上面清晰地展示了变量a现在指向到hi()函数中的greet()函数
#现在试试这个
print(a()) # 相当于调用的hi()()
#outputs: now you are in the greet() function
代码解释:
-
再次看看这个代码。在if/else语句中我们返回greet和welcome,而不是greet()和welcome()。 -
为什么那样?
这是因为当你把一对小括号放在后面,这个函数就会执行 然而如果你不放括号在它后面,那它可以被到处传递,并且可以赋值给别的变量而不去执行它。
-
当 a=hi()时,由于hi后面有小括号,所以hi()会执行,hi()的返回值赋值给变量a;由于name是默认参数,所以函数greet返回了,赋值给变量a,a就代表greet函数; -
print(a()),就代表执行greet(),且输出greet()的返回值;
9.4 将函数作为参数传递给另一个函数(装饰器的前提)
-
将函数作为参数传递给另一个函数A中,然后在函数A中执行接收过来的参数(函数); -
这也算是一个装饰器了,执行doSomethingBeforeHi(hi)方法实现了:在执行hi函数之前执行了一起其他的事情;
#!/usr/bin/env python
#coding:utf-8
def hi():
return"hi yasoob!"
# 该方法接收个函数作为参数
def doSomethingBeforeHi(func):
print("I am doing some boring work before executing hi()")
print(func())
doSomethingBeforeHi(hi)
#outputs:I am doing some boring work before executing hi()
# hi yasoob!
9.5 第一个装饰器
-
装饰器也是一个函数,这个函数中还有一个函数,用于实现装饰; -
装饰器,当python从上向下解析的时候,会执行这个装饰器的
也就是说,当python从上向下解析文件的时候,哪怕不调用被装饰的函数,但是也会执行这个装饰器的 装饰器,不是调用被装饰的函数的是调用,而是解析的时候就调用这个装饰器
#!/usr/bin/env python
#coding:utf-8
# 装饰器,参数就是要被装饰的函数
def a_new_decorator(a_func):
# 真正实现装饰功能
defwrapTheFunction():
print("执行a_func()函数之前做些什么")# 调用前装饰
a_func()# 执行被装饰的函数
print("执行a_func()函数之后做些什么")#调用后装饰
return wrapTheFunction
defa_function_requiring_decoration():
print("装饰我")
a_function_requiring_decoration()
#outputs: "装饰我"
# a_function_requiring_decoration()被wrapTheFunction()给包装了
a_function_requiring_decoration = a_new_decorator(a_function_requiring_decoration)
a_function_requiring_decoration()
""" outputs
执行a_func()函数之前做些什么
装饰我
执行a_func()函数之后做些什么
9.6 使用@装饰器函数 来使用装饰器:语法糖
#!/usr/bin/env python
#coding:utf-8
def a_new_decorator(a_func):
def wrapTheFunction():
print("执行a_func()函数之前做些什么")
a_func()
print("执行a_func()函数之后做些什么")
return wrapTheFunction
# 通过@装饰器来装饰一个函数等价于
# a_function_requiring_decoration = a_new_decorator(a_function_requiring_decoration)
@a_new_decorator
def a_function_requiring_decoration():
print("装饰我")
a_function_requiring_decoration()
"""
执行a_func()函数之前做些什么
装饰我
执行a_func()函数之后做些什么
"""
@方式内部实现原理
-
1.上面的代码,Python解释器会从上向下开始解释代码; -
2.当执行到 def a_new_decorator(a_func)
的时候,会把这个函数加载到内存,然后继续向下执行; -
3.执行到 @a_new_decorator
的时候,会把其下紧跟的函数def a_function_requiring_decoration()
函数作为参数,传递到a_new_decorator
方法中去执行,然后把返回值(是一个函数)赋值给a_function_requiring_decoration
函数,此时已经装饰完毕,就等待调用了; -
4.当调用 a_function_requiring_decoration
函数的时候,其实调用的是步骤3返回的函数(装饰完毕的函数)。
9.7 使用工具函数来使用装饰器
#!/usr/bin/env python
#coding:utf-8
from functools import wraps
def a_new_decorator(a_func):
@wraps(a_func)
def wrapTheFunction():
print("I am doing some boring work before executing a_func()")
a_func()
print("I am doing some boring work after executing a_func()")
return wrapTheFunction
@a_new_decorator
def a_function_requiring_decoration():
"""Hey yo! Decorate me!"""
print("I am the function which needs some decoration to "
"remove my foul smell")
print(a_function_requiring_decoration.__name__)
# Output: a_function_requiring_decoration
9.8 装饰器蓝本规范
-
注意:@wraps接受一个函数来进行装饰,并加入了复制函数名称,注释文档,参数列表等功能。 -
这可以让我们在装饰器里面访问在装饰之前的函数的属性。
#!/usr/bin/env python
#coding:utf-8
from functools import wraps
def decorator_name(f):
@wraps(f)
def decorated(*args,**kwargs):
ifnot can_run:
return"Function will not run"
return f(*args,**kwargs)
return decorated
@decorator_name
def func():
return("Function is running")
can_run =True
print(func())
# Output: Function is running
can_run =False
print(func())
9.9 在函数中嵌入装饰器
#!/usr/bin/env python
#coding:utf-8
from functools import wraps
def logit(logfile='out.log'):
def logging_decorator(func):
@wraps(func)
def wrapped_function(*args,**kwargs):
log_string = func.__name__ +" was called"
print(log_string)
# 打开logfile,并写入内容
with open(logfile,'a')as opened_file:
# 现在将日志打到指定的logfile
opened_file.write(log_string +'n')
return func(*args,**kwargs)
return wrapped_function
return logging_decorator
@logit()
def myfunc1():
pass
myfunc1()
# Output: myfunc1 was called
# 现在一个叫做 out.log 的文件出现了,里面的内容就是上面的字符串
@logit(logfile='func2.log')
defmyfunc2():
pass
myfunc2()
# Output: myfunc2 was called
# 现在一个叫做 func2.log 的文件出现了,里面的内容就是上面的字符串
9.10 装饰器类
#!/usr/bin/env python
#coding:utf-8
from functools import wraps
class logit(object):
def __init__(self, logfile='out.log'):
self.logfile = logfile
def __call__(self, func):
@wraps(func)
def wrapped_function(*args,**kwargs):
log_string = func.__name__ +" was called"
print(log_string)
# 打开logfile并写入
with open(self.logfile,'a')as opened_file:
# 现在将日志打到指定的文件
opened_file.write(log_string +'n')
# 现在,发送一个通知
self.notify()
return func(*args,**kwargs)
return wrapped_function
def notify(self):
# logit只打日志,不做别的
pass
-
实现logit的子类,可以实现发送email的功能
class email_logit(logit):
'''
一个logit的实现版本,可以在函数调用时发送email给管理员
'''
def __init__(self, email='admin@myproject.com',*args,**kwargs):
self.email = email
super(logit, self).__init__(*args,**kwargs)
def notify(self):
# 发送一封email到self.email
# 这里就不做实现了
pass
9.11 给带返回值的函数装饰
-
装饰器本身是一个函数; -
装饰器的功能就是在函数执行前和执行后,需要做些什么 -
装饰器函数的实现:
装饰器函数接受一个参数,这个参数实际上是要装饰的函数; 装饰器函数内部又有一个内部函数,这个内部函数调用装饰器函数接受的函数; 然后在内部函数内来实现一些装饰器的功能,然后内部函数返回内部函数名; 装饰器可以获得原函数的返回值,然后返回。
#_*_coding:utf-8_*_
#使用函数完成一个装饰器,这个装饰器的名字定义为outer
def outer(fun): #这里的参数,是要装饰的函数名
def wrapper(arg): #这里的参数,是要装饰的函数的参数
print '验证'
result = fun(arg) # 调用被装饰的参数
return "result values: " + result
return wrapper # 返回装饰器内部方法
@outer
def test(arg):
print '被装饰'
return 'test的返回值'
print(test('hello'))
-
上述同样的方法,通过闭包来调用
#_*_coding:utf-8_*_
#使用函数完成一个装饰器,这个装饰器的名字定义为outer
def outer(fun): #这里的参数,是要装饰的函数名
def wrapper(arg): #这里的参数,是要装饰的函数的参数
print '验证'
result = fun(arg) # 调用被装饰的参数
return "result values: " + result
return wrapper # 返回装饰器内部方法
@outer
def test(arg):
print '被装饰'
return 'test的返回值'
print(outer(test)) #<function wrapper at 0x00000000027B96D8>
sp = outer(test) # sp就是wrapper函数
resp = sp('测试') #这里调用wrapper函数
print resp
9.12 装饰器-没有内嵌包装函数的装饰器
#_*_coding:utf-8_*_
def deco(func):
print('before myfunc() called')
func()
print('after myfunc() called')
return func
@deco
def myfunc():
print ('myfunc() called')
myfunc()
# 结果
#before myfunc() called
#myfunc() called
#after myfunc() called
#myfunc() called
9.13 让装饰器带参数,是装饰器的参数
-
带有参数的装饰器,能够起到在运行时,有不同的功能。 -
小技巧:
先写不带参数的装饰器,写完了后 再装饰器外面包装一层即可,参数,在第一步写好的装饰器中使用即可
#_*_coding:utf-8_*_
def deco(arg): # arg是装饰器的参数
def _deco(func):#这里是内部的装饰器函数
def __deco():
print('before %s called [%s]' %(func.__name__, arg))
func()
print('after %s called [%s]' %(func.__name__, arg))
return __deco
return _deco
@deco("mymodule")
def myfunc():
print('myfunc() called')
@deco('module2')
def myfunc2():
print('myfunc2 called')
myfunc()
myfunc2()
#结果
#before myfunc called [mymodule]
#myfunc() called
#after myfunc called [mymodule]
#before myfunc2 called [module2]
#myfunc2 called
#after myfunc2 called [module2]
另一个例子
def set_level(level_num):
def set_func(func):
def call_func(*args, **kwargs):
if level_num == 1:
print("----权限级别1,验证----")
elif level_num == 2:
print("----权限级别2,验证----")
return func()
return call_func
return set_func
# 带有参数的装饰器装饰过程分为2步:
# 1. 调用set_level函数,把1当做实参
# 2. set_level返回一个装饰器的引用,即set_func
# 3. 用返回的set_func对test1函数进行装饰(装饰过程与之前一样)
@set_level(1)
def test1():
print("-----test1---")
return "ok"
@set_level(2)
def test2():
print("-----test2---")
return "ok"
test1()
test2()
9.14 类当做装饰器
把对象当做方法来调用
-
通过 __call__
方法来实现
# _*_coding:utf-8_*_
class Person():
def __call__(self):
print('__call__方法让对象可以按函数方式调用,调用的就是这个')
t = Person()
t()
类当做装饰器
# _*_coding:utf-8_*_
class Person():
# 当类做装饰器的时候,这个类的构造函数中一定要接受一个参数
# 这个参数就是要装饰的方法的方法名
def __init__(self, func):
print('--初始化-- %s' %func)
func()
self.__func = func
def __call__(self):
print('__call__方法让对象可以按函数方式调用,调用的就是这个')
self.__func()
@Person
def hello(): # 这种写法等价于 hello = Person(hello)
print('---hello---')
hello() # 这里调用的是Person的__call__
-
当用Person来装作装饰器对hello函数进行装饰的时候,首先会创建Person的实例对象
并且会把hello这个函数名当做参数传递到 __init__
方法中即在 __init__
方法中的func变量指向了hello函数体
-
hello函数相当于指向了用Person创建出来的实例对象 -
当在使用hello()进行调用时,就相当于让这个对象按照函数的方式调用,因此会调用这个Person对象的 __call__
方法 -
为了能够在 __call__
方法中调用原来hello指向的函数体,所以在__init__
方法中就需要一个实例属性来保存这个函数体的引用 -
所以才有了 self.__func = func
这句代码,从而在调用__call__
方法中能够调用到hello之前的函数体
10. 九步学装饰器
1. 第一步:最简单的函数,准备附加额外功能
#!/usr/bin/env python
#coding:utf-8
'''示例1: 最简单的函数,表示调用了两次'''
def myfunc():
print("myfunc() called.")
myfunc()
myfunc()
2. 第二步:使用装饰函数在函数执行前和执行后分别附加额外功能
#!/usr/bin/env python
#coding:utf-8
'''示例2: 替换函数(装饰)
装饰函数的参数是被装饰的函数对象,返回原函数对象
装饰的实质语句: myfunc = deco(myfunc)'''
def deco(func):
print("before myfunc() called.")
func()
print("after myfunc() called.")
return func
def myfunc():
print("myfunc() called.")
myfunc = deco(myfunc)
myfunc()
myfunc()
"""
before myfunc() called.
myfunc() called.
after myfunc() called.
myfunc() called.
myfunc() called.
"""
3. 第三步:使用语法糖@来装饰函数
#!/usr/bin/env python
#coding:utf-8
'''示例3: 使用语法糖@来装饰函数,相当于“myfunc = deco(myfunc)”
但发现新函数只在第一次被调用,且原函数多调用了一次'''
def deco(func):
print("before myfunc() called.")
func()
print(" after myfunc() called.")
return func
@deco
def myfunc():
print(" myfunc() called.")
myfunc()
myfunc()
"""
before myfunc() called.
myfunc() called.
after myfunc() called.
myfunc() called.
myfunc() called.
"""
4. 第四步:使用内嵌包装函数来确保每次新函数都被调用
#!/usr/bin/env python
#coding:utf-8
'''示例4: 使用内嵌包装函数来确保每次新函数都被调用,
内嵌包装函数的形参和返回值与原函数相同,装饰函数返回内嵌包装函数对象'''
def deco(func):
def _deco():
print("before myfunc() called.")
func()
print(" after myfunc() called.")
# 不需要返回func,实际上应返回原函数的返回值
return _deco
@deco
def myfunc():
print(" myfunc() called.")
return 'ok'
myfunc()
myfunc()
"""
before myfunc() called.
myfunc() called.
after myfunc() called.
before myfunc() called.
myfunc() called.
after myfunc() called.
"""
5. 第五步:对带参数的函数进行装饰
#!/usr/bin/env python
#coding:utf-8
'''示例5: 对带参数的函数进行装饰,
内嵌包装函数的形参和返回值与原函数相同,装饰函数返回内嵌包装函数对象'''
def deco(func):
def _deco(a, b):
print("before myfunc() called.")
ret = func(a, b)
print(" after myfunc() called. result: %s" % ret)
return ret
return _deco
@deco
def myfunc(a, b):
print(" myfunc(%s,%s) called." % (a, b))
return a + b
myfunc(1, 2)
myfunc(3, 4)
"""
before myfunc() called.
myfunc(1,2) called.
after myfunc() called. result: 3
before myfunc() called.
myfunc(3,4) called.
after myfunc() called. result: 7
"""
6. 第六步:对参数数量不确定的函数进行装饰
#!/usr/bin/env python
#coding:utf-8
'''示例6: 对参数数量不确定的函数进行装饰,
参数用(*args, **kwargs),自动适应变参和命名参数'''
def deco(func):
def _deco(*args, **kwargs):
print("before %s called." % func.__name__)
ret = func(*args, **kwargs)
print(" after %s called. result: %s" % (func.__name__, ret))
return ret
return _deco
@deco
def myfunc(a, b):
print(" myfunc(%s,%s) called." % (a, b))
return a+b
@deco
def myfunc2(a, b, c):
print(" myfunc2(%s,%s,%s) called." % (a, b, c))
return a+b+c
myfunc(1, 2)
myfunc(3, 4)
myfunc2(1, 2, 3)
myfunc2(3, 4, 5)
"""
before myfunc called.
myfunc(1,2) called.
after myfunc called. result: 3
before myfunc called.
myfunc(3,4) called.
after myfunc called. result: 7
before myfunc2 called.
myfunc2(1,2,3) called.
after myfunc2 called. result: 6
before myfunc2 called.
myfunc2(3,4,5) called.
after myfunc2 called. result: 12
"""
7. 第七步:让装饰器带参数
#!/usr/bin/env python
#coding:utf-8
'''示例7: 在示例4的基础上,让装饰器带参数,
和上一示例相比在外层多了一层包装。
装饰函数名实际上应更有意义些'''
def deco(arg):
def _deco(func):
def __deco():
print("before %s called [%s]." % (func.__name__, arg))
func()
print(" after %s called [%s]." % (func.__name__, arg))
return __deco
return _deco
@deco("mymodule")
def myfunc():
print(" myfunc() called.")
@deco("module2")
def myfunc2():
print(" myfunc2() called.")
myfunc()
myfunc2()
"""
before myfunc called [mymodule].
myfunc() called.
after myfunc called [mymodule].
before myfunc2 called [module2].
myfunc2() called.
after myfunc2 called [module2].
"""
8. 第八步:让装饰器带 类 参数
#!/usr/bin/env python
#coding:utf-8
'''示例8: 装饰器带类参数'''
class locker:
def __init__(self):
print("locker.__init__() should be not called.")
@staticmethod
def acquire():
print("locker.acquire() called.(这是静态方法)")
@staticmethod
def release():
print(" locker.release() called.(不需要对象实例)")
def deco(cls):
'''cls 必须实现acquire和release静态方法'''
def _deco(func):
def __deco():
print("before %s called [%s]." % (func.__name__, cls))
cls.acquire()
try:
return func()
finally:
cls.release()
return __deco
return _deco
@deco(locker)
def myfunc():
print(" myfunc() called.")
myfunc()
myfunc()
"""
before myfunc called [__main__.locker].
locker.acquire() called.(这是静态方法)
myfunc() called.
locker.release() called.(不需要对象实例)
before myfunc called [__main__.locker].
locker.acquire() called.(这是静态方法)
myfunc() called.
locker.release() called.(不需要对象实例)
"""
9. 第九步:装饰器带类参数,并分拆公共类到其他py文件中,同时演示了对一个函数应用多个装饰器
-
mylocker.py
#!/usr/bin/env python
#coding:utf-8
'''mylocker.py: 公共类 for 示例9.py'''
class mylocker:
def __init__(self):
print("mylocker.__init__() called.")
@staticmethod
def acquire():
print("mylocker.acquire() called.")
@staticmethod
def unlock():
print(" mylocker.unlock() called.")
class lockerex(mylocker):
@staticmethod
def acquire():
print("lockerex.acquire() called.")
@staticmethod
def unlock():
print(" lockerex.unlock() called.")
def lockhelper(cls):
'''cls 必须实现acquire和release静态方法'''
def _deco(func):
def __deco(*args, **kwargs):
print("before %s called." % func.__name__)
cls.acquire()
try:
return func(*args, **kwargs)
finally:
cls.unlock()
return __deco
return _deco
-
test.py
#!/usr/bin/env python
#coding:utf-8
'''示例9: 装饰器带类参数,并分拆公共类到其他py文件中
同时演示了对一个函数应用多个装饰器'''
from mylocker import *
class example:
@lockhelper(mylocker)
def myfunc(self):
print(" myfunc() called.")
@lockhelper(mylocker)
@lockhelper(lockerex)
def myfunc2(self, a, b):
print(" myfunc2() called.")
return a + b
if __name__ == "__main__":
a = example()
a.myfunc()
print(a.myfunc())
print(a.myfunc2(1, 2))
print(a.myfunc2(3, 4))
11. 元类
11.1 类也是对象
# _*_coding:utf-8_*_
class Person(object):
print ('person test')
def __init__(self):
self.name='hello'
# 当创建了这个类以后,直接运行,就会输出
# person test
# 说明了 类就是对象
-
通过上面的代码 -
将在内存中创建一个对象,名字就是Person。这个对象(类对象Person)拥有创建对象(实例对象)的能力。 -
但是,它的本质仍然是一个对象,于是乎你可以对它做如下的操作:
你可以将它赋值给一个变量 你可以拷贝它 你可以为它增加属性 你可以将它作为函数参数进行传递
11.2 在方法中动态创建类
-
类也是对象,你可以在运行时动态的创建它们,就像其他任何对象一样。 -
首先,你可以在函数中创建类,使用class关键字即可。
# _*_coding:utf-8_*_
def create_class(name):
if name == 'foo':
class Foo(object):
pass
return Foo # 返回类
else:
class Bar(object):
pass
return Bar
MyClass = create_class('foo')
print(MyClass) # <class '__main__.Foo'>
print(MyClass()) # <__main__.Foo object at 0x0000000001CF3390>
11.3 使用type创建类
-
type()
可以看见对象的类型,但是还可以动态创建类。 -
type可以接受一个类的描述作为参数,然后返回一个类。(要知道,根据传入参数的不同,同一个函数拥有两种完全不同的用法是一件很傻的事情,但这在Python中是为了保持向后兼容性) -
格式: type(类名, 由父类名称组成的元组(针对继承的情况,可以为空),包含属性的字典(名称和值))
使用type来创建无属性的类
# _*_coding:utf-8_*_
# MyClass要创建的类名
# () 元祖,要继承的父类
# {} 包含属性的字典(名称和值))
MyClass = type("MyClass",(),{})
c = MyClass()
print(type(c)) # <class '__main__.MyClass'>
使用type创建有属性的类
-
属性key-value都是加双引号,数字可以不加
# _*_coding:utf-8_*_
MyClass = type('MyClass', (), {"num":0})
c = MyClass()
print(type(c)) # <class '__main__.MyClass'>
使用type创建一个带有普通方法的类
-
方法的value不加双引号,代表是引用。 -
添加静态方法或动态方法,直接在外面创建好静态方法或动态方法,然后直接添加即可。
# _*_coding:utf-8_*_
def printNum(self):
print("num:%d"%self.num)
MyClass = type('MyClass', (), {"num":10, "printNum":printNum})
c = MyClass()
print(type(c)) # <class '__main__.MyClass'>
c.printNum() # num:10
11.4 __metaclass__
属性
-
你可以在定义一个类的时候为其添加 __metaclass__
属性。
class Foo(object):
__metaclass__ = something…
...省略...
-
如果你这么做了,Python就会用元类来创建类Foo。 -
小心点,这里面有些技巧。你首先写下class Foo(object),但是类Foo还没有在内存中创建。 -
Python会在类的定义中寻找 __metaclass__
属性,如果找到了,Python就会用它来创建类Foo,如果没有找到,就会用内建的type来创建这个类。 -
把下面这段话反复读几次。当你写如下代码时 :
class Foo(Bar):
pass
-
Python做了如下的操作:
Foo中有 __metaclass__
这个属性吗?如果是,Python会通过__metaclass__
创建一个名字为Foo的类(对象)如果Python没有找到 __metaclass__
,它会继续在Bar(父类)中寻找__metaclass__
属性,并尝试做和前面同样的操作。如果Python在任何父类中都找不到 __metaclass__
,它就会在模块层次中去寻找__metaclass__
,并尝试做同样的操作。如果还是找不到 __metaclass__
,Python就会用内置的type来创建这个类对象。
-
这里的 __metaclass__
中到底写什么呢。
可以以创建一个类的东西。那么什么可以用来创建一个类呢?type,或者任何使用到type或者子类化type都可以。
11.5 自定义元类
-
元类的主要目的就是为了当创建类时能够自动地改变类。 -
假想一个很傻的例子,你决定在你的模块里所有的类的属性都应该是大写形式。 -
有好几种方法可以办到,但其中一种就是通过在模块级别设定 __metaclass__
。 -
采用这种方法,这个模块中的所有类都会通过这个元类来创建,我们只需要告诉元类把所有的属性都改成大写形式就万事大吉了。 -
幸运的是, __metaclass__
实际上可以被任意调用,它并不需要是一个正式的类。
python2中
#-*- coding:utf-8 -*-
def upper_attr(class_name, class_parents, class_attr):
# class_name 会保存类的名字 Foo
# class_parents 会保存类的父类 object
# class_attr 会以字典的方式保存所有的类属性
# 遍历属性字典,把不是__开头的属性名字变为大写
new_attr = {}
for name, value in class_attr.items():
if not name.startswith("__"):
new_attr[name.upper()] = value
# 调用type来创建一个类
return type(class_name, class_parents, new_attr)
class Foo(object):
__metaclass__ = upper_attr # 设置Foo类的元类为upper_attr
bar = 'bip'
print(hasattr(Foo, 'bar'))
print(hasattr(Foo, 'BAR'))
f = Foo()
print(f.BAR)
python3中
#-*- coding:utf-8 -*-
def upper_attr(class_name, class_parents, class_attr):
#遍历属性字典,把不是__开头的属性名字变为大写
new_attr = {}
for name,value in class_attr.items():
if not name.startswith("__"):
new_attr[name.upper()] = value
#调用type来创建一个类
return type(class_name, class_parents, new_attr)
class Foo(object, metaclass=upper_attr):
bar = 'bip'
print(hasattr(Foo, 'bar'))
print(hasattr(Foo, 'BAR'))
f = Foo()
print(f.BAR)
用一个真正的class来当做元类
-
拦截类的创建 -
修改类 -
返回修改之后的类
#coding=utf-8
class UpperAttrMetaClass(type):
# __new__ 是在__init__之前被调用的特殊方法
# __new__是用来创建对象并返回之的方法
# 而__init__只是用来将传入的参数初始化给对象
# 你很少用到__new__,除非你希望能够控制对象的创建
# 这里,创建的对象是类,我们希望能够自定义它,所以我们这里改写__new__
# 如果你希望的话,你也可以在__init__中做些事情
# 还有一些高级的用法会涉及到改写__call__特殊方法,但是我们这里不用
def __new__(cls, class_name, class_parents, class_attr):
# 遍历属性字典,把不是__开头的属性名字变为大写
new_attr = {}
for name, value in class_attr.items():
if not name.startswith("__"):
new_attr[name.upper()] = value
# 方法1:通过'type'来做类对象的创建
return type(class_name, class_parents, new_attr)
# 方法2:复用type.__new__方法
# 这就是基本的OOP编程,没什么魔法
# return type.__new__(cls, class_name, class_parents, new_attr)
# python3的用法
class Foo(object, metaclass=UpperAttrMetaClass):
bar = 'bip'
# python2的用法
# class Foo(object):
# __metaclass__ = UpperAttrMetaClass
# bar = 'bip'
print(hasattr(Foo, 'bar'))
# 输出: False
print(hasattr(Foo, 'BAR'))
# 输出:True
f = Foo()
print(f.BAR)
# 输出:'bip'
原文始发于微信公众号(Python之家):Python基础-28-提高
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/198451.html