python遇到的黑魔法
metaclass
在python2中可以对__metaclass__进行赋值来实现对你创造的类进行一些特殊的事情,比如你想规定限定你的类里方法名称全部要大写,真么奇怪的事情怎么做到?
# the metaclass will automatically get passed the same argument
# that you usually pass to `type`
def upper_attr(future_class_name, future_class_parents, future_class_attr):
"""
Return a class object, with the list of its attribute turned
into uppercase.
"""
# pick up any attribute that doesn't start with '__' and uppercase it
uppercase_attr = {}
for name, val in future_class_attr.items():
if not name.startswith('__'):
uppercase_attr[name.upper()] = val
else:
uppercase_attr[name] = val
# let `type` do the class creation
return type(future_class_name, future_class_parents, uppercase_attr)
__metaclass__ = upper_attr # this will affect all classes in the module
class Foo(): # global __metaclass__ won't work with "object" though
# but we can define __metaclass__ here instead to affect only this class
# and this will work with "object" children
bar = 'bip'
print(hasattr(Foo, 'bar'))
# Out: False
print(hasattr(Foo, 'BAR'))
# Out: True
f = Foo()
print(f.BAR)
# Out: 'bip'
最好还是在类里面给__metaclass__赋值,不然整个模块的类都带有这个特性
在python 3中这个神奇的变量被移除但是可以用另一种形式使用它
class Foo(object, metaclass=something, kwarg1=value1, kwarg2=value2):
...
对上面的例子在python3里重现就是如下代码
# the metaclass will automatically get passed the same argument
# that you usually pass to `type`
def upper_attr(future_class_name, future_class_parents, future_class_attr):
"""
Return a class object, with the list of its attribute turned
into uppercase.
"""
# pick up any attribute that doesn't start with '__' and uppercase it
uppercase_attr = {}
for name, val in future_class_attr.items():
if not name.startswith('__'):
uppercase_attr[name.upper()] = val
else:
uppercase_attr[name] = val
# let `type` do the class creation
return type(future_class_name, future_class_parents, uppercase_attr)
class Foo(metaclass=upper_attr): # global __metaclass__ won't work with "object" though
bar = 'bip'
print(hasattr(Foo, 'bar'))
# Out: False
print(hasattr(Foo, 'BAR'))
# Out: True
f = Foo()
print(f.BAR)
# Out: 'bip'
思考一下如何用类实现Meta?
这一切的一切的实现都基于type这个神奇的类,他不仅仅可以用来判别一个变量的类型,他还可以创建类,下面这两个创建类的方式其实是等价的
class Parent():
pass
class MyClass(Parent):
name = 'yang'
Eg_2 = type('MyClass',(Parent,),{'name':'yang'})
print(type(MyClass)) #<class 'type'>
print(type(Eg_2)) #<class 'type'>
demo1 = MyClass()
demo2 = Eg_2()
print(type(demo1)) #<class '__main__.MyClass'>
print(type(demo2)) #<class '__main__.MyClass'>
这里另外举出可以带参数的例子,以及另一种改变继承子类创造过程的简便方法,顺带说一下None的理解
##########################################################################
import platform
print (platform.python_version())
class Meta(type):
def __new__(cls,name,bases,attrs,**kwargs):
print(name+"come into the Meta!")
if kwargs:
print(kwargs)
return super().__new__(cls, name, bases, attrs)#这里不能加kwargs
class Demo(metaclass=Meta,private=True,key1=1,key2=2):
pass
#########################################################################
#元模型太笨重,用下面的__init_subclass__方法来做点事吧~~~
class Hook:
def __init_subclass__(cls, **kwargs):
for k, v in kwargs.items():
type.__setattr__(cls, k, v)
class A(Hook, name="satori", age=16):
pass
print(A.name) # satori
print(A.age) # 16
# python None 类型理解
x=()
y=None
print(x is None)#false
print(y is None)#true
print(not x is None)#true x等于None, False, 空字符串"", 0, 空列表[], 空字典{}, 空元组()都会是true
print(not y is None)#false
print(not x)#true
print(not y)#true
print(x is not None)#true
装饰器
非常方便~比如我有一个函数,我让这个函数执行前做不同的事情,并且通过不同的参数来控制,看下面的代码示例:
#假设这是我的一个打印消息的函数,并返回一个字符串
def recv():
print("hello")
#最简洁的装饰器
def extend(func):
def decorate():
print("i will be printed before hello~")
func()
return decorate
decorate= extend(recv)
decorate()
python中有个语法糖可以让上面的代码变成这个样子,输出是等价的
#最简洁的装饰器
def extend(func):
def decorate():
print("i will be printed before hello~")
func()
return decorate
@extend
def recv():
print("hello")
recv()
下面我的recv有参数有返回值怎么办
#最简洁的装饰器
def extend(func):
def decorate(msg):
print("i will be printed before hello~")
return func(msg)
return decorate
@extend
def recv(msg):
print("hello")
return msg
ret = recv("msg")
print(ret) #msg
传参更一般的形式你可以把msg替换成 *param,**kwargs 这种~
下面还有最后一种更复杂的情况,我想在语法糖里加参数来使我的装饰效果更灵活
#最简洁的装饰器
def extend(para):
def decorate(func):
def real(msg):
print("i will be printed before hello~")
if para==2:
print("wow para:"+str(para))
elif para==1:
print("dodo para:"+str(para))
else
print("lalalallal~")
return func(msg)
return real
return decorate
@extend(2)
def recv(msg):
print("hello")
return msg
ret = recv("msg")
print(ret) #msg
###########################
i will be printed before hello~
wow para:2
hello
msg
把@extend后面的参数改成1就会有不一样的表现哦~
解释一下,上面的过程其实等价于
decorate = extend(2)
real = decorate(recv)
ret = real("msg")
print(ret) #msg
###########################
i will be printed before hello~
wow para:2
hello
msg
最后,常常在最里面一层加上一个python自带的装饰器,来使装饰后函数名不变,而且最里面一层常常用wrapper来命名,我们的最终版本是这个样子
import functools
def extend(para):
def decorate(func):
@functools.wraps(func)
def wrapper(msg):
print("i will be printed before hello~")
if para==2:
print("wow para:"+str(para))
elif para==1:
print("dodo para:"+str(para))
else:
print("lalalallal~")
return func(msg)
return wrapper
return decorate
@extend(2)
def recv(msg):
print("hello")
return msg
ret = recv("msg")
print(ret) #msg
#注释掉@functools.wraps(func)打印下面内容跟不注释有区别么~
print(recv.__name__)
猴子补丁
(一般情况不建议使用会弄乱代码,测试时可以用)