对象自省
自省(introspection),在计算机编程领域里,是指在运行时来判断一个对象的类型的能力。它是Python的强项之一。Python中所有一切都是一个对象,而且我们可以仔细勘察那些对象。Python还包含了许多内置函数和模块来帮助我们。
dir
在这个小节里我们会学习到dir
以及它在自省方面如何给我们提供便利。
它是用于自省的最重要的函数之一。它返回一个列表,列出了一个对象所拥有的属性和方法。这里是一个例子:
my_list = [1, 2, 3]
dir(my_list)
# Output: ['__add__', '__class__', '__contains__', '__delattr__', '__delitem__',
# '__delslice__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__',
# '__getitem__', '__getslice__', '__gt__', '__hash__', '__iadd__', '__imul__',
# '__init__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__',
# '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__',
# '__setattr__', '__setitem__', '__setslice__', '__sizeof__', '__str__',
# '__subclasshook__', 'append', 'count', 'extend', 'index', 'insert', 'pop',
# 'remove', 'reverse', 'sort']
上面的自省给了我们一个列表对象的所有方法的名字。当你没法回忆起一个方法的名字,这会非常有帮助。如果我们运行dir()而不传入参数,那么它会返回当前作用域的所有名字。
type和id
type函数返回一个对象的类型。举个例子:
print(type(''))
# Output: <type 'str'>
print(type([]))
# Output: <type 'list'>
print(type({}))
# Output: <type 'dict'>
print(type(dict))
# Output: <type 'type'>
print(type(3))
# Output: <type 'int'>
id()
函数返回任意不同种类对象的唯一ID,举个例子:
name = "Yasoob"
print(id(name))
# Output: 139972439030304
inspect模块
inspect模块也提供了许多有用的函数,来获取活跃对象的信息。比方说,你可以查看一个对象的成员,只需运行:
import inspect
print(inspect.getmembers(str))
# Output: [('__add__', <slot wrapper '__add__' of ... ...
还有好多个其他方法也能有助于自省。如果你愿意,你可以去探索它们。
枚举
枚举(enumerate
)是Python内置函数。它的用处很难在简单的一行中说明,但是大多数的新人,甚至一些高级程序员都没有意识到它。
它允许我们遍历数据并自动计数,例如:
for counter, value in enumerate(some_list):
print(counter, value)
也可接受一些可选参数
my_list = ['apple', 'banana', 'grapes', 'pear']
for c, value in enumerate(my_list, 1):
print(c, value)
# 输出:
(1, 'apple')
(2, 'banana')
(3, 'grapes')
(4, 'pear')
上面这个可选参数允许我们定制从哪个数字开始枚举。
你还可以用来创建包含索引的元组列表, 例如:
my_list = ['apple', 'banana', 'grapes', 'pear']
counter_list = list(enumerate(my_list, 1))
print(counter_list)
# 输出: [(1, 'apple'), (2, 'banana'), (3, 'grapes'), (4, 'pear')]
try/else从句
我们常常想在没有触发异常的时候执行一些代码。这可以很轻松地通过一个else从句来达到。
有人也许问了:如果你只是想让一些代码在没有触发异常的情况下执行,为啥你不直接把代码放在try里面呢?
回答是,那样的话这段代码中的任意异常都还是会被try捕获,而你并不一定想要那样。
大多数人并不使用else从句,而且坦率地讲我自己也没有大范围使用。这里是个例子:
try:
print('I am sure no exception is going to occur!')
except Exception:
print('exception')
else:
# 这里的代码只会在try语句里没有触发异常时运行,
# 但是这里的异常将 *不会* 被捕获
print('This would only run if no exception occurs. And an error here '
'would NOT be caught.')
finally:
print('This would be printed in every case.')
# Output: I am sure no exception is going to occur!
# This would only run if no exception occurs.
# This would be printed in every case.
else从句只会在没有异常的情况下执行,而且它会在finally语句之前执行。
For-else
else从句会在循环正常结束时执行。这意味着,循环没有遇到任何break
基本结构:
for item in container:
if search_something(item):
# Found it!
process(item)
break
else:
# Didn't find anything..
not_found_in_container()
简单案例
for n in range(2, 10):
for x in range(2, n):
if n % x == 0:
print(n, 'equals', x, '*', n / x)
break
闭包
一个函数和它的环境变量合在一起,就构成了一个闭包(closure)。
协程
Python中的协程和生成器很相似但又稍有不同。主要区别在于:
- 生成器是数据的生产者
- 协程则是数据的消费者
首先我们先来回顾下生成器的创建过程。我们可以这样去创建一个生成器:
def fib():
a, b = 0, 1
while True:
yield a
a, b = b, a+b
然后我们经常在for循环中这样使用它:
for i in fib():
print i
这样做不仅快而且不会给内存带来压力,因为我们所需要的值都是动态生成的而不是将他们存储在一个列表中。更概括的说如果现在我们在上面的例子中使用yield便可获得了一个协程。协程会消费掉发送给它的值。Python实现的grep就是个很好的例子:
def grep(pattern):
print("Searching for", pattern)
while True:
line = (yield)
if pattern in line:
print(line)
等等!yield返回了什么?啊哈,我们已经把它变成了一个协程。它将不再包含任何初始值,相反要从外部传值给它。我们可以通过send()方法向它传值。这有个例子:
search = grep('coroutine')
next(search)
#output: Searching for coroutine
search.send("I love you")
search.send("Don't you love me?")
search.send("I love coroutine instead!")
#output: I love coroutine instead!
发送的值会被yield接收。我们为什么要运行next()方法呢?这样做正是为了启动一个协程。就像协程中包含的生成器并不是立刻执行,而是通过next()方法来响应send()方法。因此,你必须通过next()方法来执行yield表达式。
我们可以通过调用close()方法来关闭一个协程。像这样:
search = grep('coroutine')
search.close()