Python中的一切都是对象,它们要么是类的实例,要么是元类的实例,除了type。type实际上是它自己的元类,在纯Python环境中这可不是你能够做到的,这是通过在实现层面耍一些小手段做到的。其次,元类是很复杂的。对于非常简单的类,你可能不希望通过使用元类来对类做修改。你可以通过其他两种技术来修改类:
1)Monkey patching
2) Class decorators
当你需要动态修改类时,99%的时间里你最好使用上面这两种技术。当然了,其实在99%的时间里你根本就不需要动态修改类
2 私有变量(__xx)
python类里的私有变量就是前面加两个下划线这样用,但是这只是在使用上的私有变量,不像Java那种只能通过内部函数修改,python的私有变量可以通过 对象._类名__参数来从外部引用。
3 type
4 推导式
推导式又称解析式,有三种
1,列表推导式
multiples = [ i for i in range(30) if i % 3 is 0 ]
2,字典推导式
mcase = {"a":10,"b":2,"c":3}
3,集合推导式
其实大括号里扩着的就是集合(set),例:
{"a","b",1}
squared =
5 装饰器(@decorate)
装饰器是python特色代表之一,非常好用,先介绍一下如何用装饰器。
函数是可以返回函数的
def hi(name="yasoob"):
def greet():
return "in greet() function"
def welcome():
return "in welcome() function"
if name == "yasoob":
return greet
else:
return welcome
a = hi()
print a
在if/else里面我们返回greet和welcome,而不是greet()和welcome(),为什么? 是因为当把小括号放到后面的时候这个函数就会执行,如果不放小括号这个函数就可以到处传递,并且可以赋给变量而不去执行。
将函数作为参数传递给另一个函数
def hi():
return "hi yasoob"
def doSomethingBefore(func):
print "I am doing something before"
print (func())
doSomethingBefore(hi)
输出:
I am doing something before
hi yasoob
装饰器就是在一个函数前后执行代码
上个例子里我们相当于创建了装饰器,现在我们稍加修改并编写一个更有用的程序。
def a_new_decorator(a_func):
def wrapTheFunction():
print "I am doing some before"
a_func()
print "I am doing some after"
return wrapTheFunction
def a_function_requiring_decoration():
print "I am in the function"
a_function_requiring_decoration = a_new_decorator(a_function_requiring_decoration)
a_function_requiring_decoration()
明白了吗? 这正是python装饰器做的事情,它们封装一个函数,并且用这样或者那样的方式修改它的行为,现在你可能疑惑,我们的代码里并没有使用@符号?那只是一个简短的方式来生成一个被装饰的函数。请见如下例子
@a_new_decorator
def a_function_requiring_decoration():
print "I am in the function"
a_function_requiring_decoration()
现在对装饰器的理解差不多了吧!但如果我们运行如下代码会存在一个问题:
print(a_function_requiring_decoration.__name__)
输出:wrapTheFunction
这并不是我们想要看到的,我们想看到的是a_function_requiring_decoration,这里的函数被wrapTheFunction替代了,它重写了我们函数的名字和注释文档(docstring)。幸运的是python提供给我们一个简单的函数来解决这个问题
from functools import wraps
def a_new_decorator(a_func):
@wraps(a_func)
def wrapTheFunction():
print "I am doing some before"
a_func()
print "I am doing some after"
return wrapTheFunction
下面我们看一下蓝本规范:
from functools import wraps
def decorator_name(f):
@wraps(f)
def decorated(*args, **kwargs):
if not 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())
can_run = False
print(func())
注意:@wraps接受一个函数来进行装饰,并加入了复制函数名称,注释文档,参数列表等等的功能。这可以让我们在装饰器里面访问在装饰器之前的函数的属性。
装饰器的使用场景:
授权(Authorization)
装饰器能有助于检查某个人是否被授权去使用一个web应用的端点(endpoint)。它们被大量使用于Flask和Django框架中。这里是一个例子来使用基于装饰器的授权:
from functools import wraps
def requires_auth(f):
@wraps(f)
def decorated(*args, **kwargs):
auth = request.authorization
if not auth or not check_auth(auth.username,auth.password):
authenticate()
return f(*args,**kwargs)
return decorated
日志(Logging)
from functools import wraps
def logit(func):
@wraps(func)
def with_logging(*args, **kwargs):
print(func.__name__ + " was called")
return func(*args, **kwargs)
return with_logging
@logit
def addition_func(x):
return x+x
addition_func(2)
在函数中嵌入装饰器
我们回到日志的例子,并创建一个包裹函数,能让我们指定一个用于输出的日志文件。
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)
with open(logfile, 'wb') as f:
f.write(log_string + '')
return func(*args, **kwargs)
return wrapped_function
return logging_decorator
@logit
def myfunc():
pass
装饰器类
现在我们有了能用于正式环境的logit装饰器,但当我们的应用的某些部分还比较脆弱时,异常也许是需要更紧急关注的事情。比方说有时候你只想打日志到一个文件,而有时你想把引起你注意的问题发送到一个email,同事也保留日志,留个记录。这是一个使用继承的场景,但目前为止我们只看到过用来构建装饰器的函数。
The lucky is! 类也可以构建装饰器,现在我们用类重新构建logit
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)
with open(self.logfile, 'wb') as f:
f.write(log_string + '')
self.notify()
return func(*args,**kwargs)
return wrapped_function
def notify(self):
# 可以做一些其它行为
pass
@logit()
def my_func():
pass
现在我们给logit创建子类,来添加email等功能
从现在起,@email_logit会在logit基础上多发送一封邮件。
注意:从以上方法中我们就可以发现__call__这种用法的好处,它在装饰器类和新写元类的时候起到了很大作用。
6 容器
python附带一个模块,它包含许多容器数据类型,名字叫做collections。我们将讨论它的作用和用法。
defaultdict:
defaultdict不需要检查key是否存在,我们一般这样用
from collections import defaultdict
ddl = defaultdict(list)
ddl["x"].append(1)
print ddl
ddd = defaultdict(dict)
ddd["x"]["a"] = 1
print ddd
defaultdict(, {'x': [1]})
defaultdict(, {'x': {'a': 1}})
Counter
counter是一个计数器,帮助我们对某项数据做统计。
from collections import Counter
c = Counter("aaaabbbc")
print c
d = {"a":1,"b":2,"c":3}
c = Counter( k for k,v in d.items())
print c
还可以用counter来统计一个文件
此处没有弄明白,需要后期补上
deque
deque提供了一个双向队列,可以从头尾两端添加或删除元素,类似于list
from collections import deque
dl = deque(range(5))
print dl
dl.popleft()
print dl
dl.pop()
print dl
dl.extendleft([-10])
print dl
dl.extend([10])
print dl
输出:
deque([0, 1, 2, 3, 4])
deque([1, 2, 3, 4])
deque([1, 2, 3])
deque([-10, 1, 2, 3])
deque([-10, 1, 2, 3, 10])
deque也可以限制列表的大小,先进先出
dl = deque(maxlen=2)
dl.append(1)
dl.append(2)
print dl
dl.append(3)
print dl
输出
deque([1, 2], maxlen=2)
deque([2, 3], maxlen=2)
namedtuple(命名元组)
正常访问一个元组和访问list一样,都是通过下标来访问,命名元组可以提供类似于字典的访问方式,和tuple一样不可变。
from collections import namedtuple
Animal = namedtuple('Animal','name age type')
perry = Animal(name='perry',age=10,type='cat')
print perry
print perry.name
一个命名元组需要两个参数,他们是元组名称和字段名称。在上面的例子中,我们的元组名称是Animal,字段名称是'name,age,type'。
namedtuple让你的元组变得自文档了。不必使用证书索引来访问一个命名元组,这让代码更易于维护。
而且,namedtuple的每个实例没有对象字典(__dict__),所以它们更轻量,与普通的元组相比,并不需要更多的内存,这使他们比字典更快。
然而,要记住它仍然是一个元组,属性在namedtuple中是不可变的,所以下面的代码不行:
perry.age = 10
命名元组(namedtuple)向后兼容元组,所以用下标访问也是可以的
print perry[0]
命名元组支持多态,可以转换为字典
print (perry._asdict())
7 上下文
上下文管理器允许你在需要的时候,精确的分配和释放资源。
使用上下文管理器最广泛的案例就是with语句。想象一下你有个需要结对执行的操作,然后还要在中间放置一段代码。
上下文管理器就是专门让你做这种事情的,举个例子:
with open('some_file', 'wb') as f:
f.write("fuck u!")
上面这段代码打开了一个文件,往里面写入了一些数据,然后关闭该文件。如果在往文件里写数据的时候发生异常,它也会尝试去关闭文件。上面的代码与下面的是等价的。
file = open('some_file', 'wb')
try:
file.write("funck u !")
finally:
file.close()
当与第一个例子比较的时候,有很多样板代码(boilerplate code)被消掉了。这就是with语句的主要优势,它确保我们的文件会被关闭,而不用关注嵌套代码如何退出。
上下文的又一用例就是资源的加锁与解锁,以及关闭已经打开的文件(就像上面的例子)
下面让我们自己实现一下上下文管理器
一个上下文管理器的类,最起码要定义__enter__,__exit__方法。
class File(object):
def __init__(self,file_name, method):
self.file_obj = open(file_name, method)
def __enter__(self):
return self.file_obj
def __exit__(self, exc_type, exc_val, exc_tb):
self.file_obj.close()
with File('demo.txt', 'wb') as f:
f.write('Hello')
我们的__exit__函数接受三个参数。这些参数对于每个上下文管理器类中的__exit__方法都是必须得,我们来谈谈在底层都发生了什么。
1,with语句先暂存了File类的__exit__方法
2,然后它调用File类的__enter__方法
3,__enter__方法返回打开文件对象
4,打开的文件对象被传递给 f
5,使用write来写文件
6,调用之前暂存的__exit__
7,__exit__关闭了文件
处理异常
我们目前还没有谈到__exit__方法的这三个参数,exc_type,exc_val,exc_tb,在with以下部分如果发生异常,python会将异常的type,value和traceback传递给__exit__方法。
它让__exit__方法来决定如何关闭文件以及是否需要其他步骤,如果没有异常这三个参数的值为None
class File(object):
def __init__(self,file_name, method):
self.file_obj = open(file_name, method)
def __enter__(self):
return self.file_obj
def __exit__(self, exc_type, exc_val, exc_tb):
print exc_val
self.file_obj.close()
return True
with File('demo.txt', 'wb') as f:
f.write_function('Hello')
当发生异常的时候with语句会采取如下步骤:
1,它把异常的type,value,traceback传递给__exit__方法
2,它让__exit__方法来处理异常
3,如果__exit__返回的是True,那么这个异常就优雅的处理了
4,如果__exit__返回的是除了True以外的其它值,那么这个异常会被抛出
基于装饰器和生成器来实现上下文管理
python有个contextlib专门用于这个,我们可以使用一个生成器函数来实现一个上下文管理器,而不是使用一个类。
from contextlib import contextmanager
@contextmanager
def open_file(name):
f = open(name,'w')
yield f
f.close()
with open_file('aaa.log') as of:
of.write("fuck u!")
这块我个人用得比较少,因为内部也是通过__enter__和__exit__来实现的。
干货分享
领取专属 10元无门槛券
私享最新 技术干货