selfteaching / selfteaching-python-camp

147 stars 881 forks source link

Day12 wxpy库的简要说明和装饰器的理解 #3764

Open sixthspace opened 5 years ago

sixthspace commented 5 years ago

一、wxpy说明书怎看 结合代码来说明一下(先通过pip安装wxpy库),代码如下:

from wxpy import *
bot = Bot()
embed() # 进入 Python 命令行

1、from wxpy import import 把一个模块的所有内容全都导入到当前的命名空间 如果写成import wxpy 那么bot = Bot() 应该写成 bot = wxpy.Bot(),这也是说明文档和实际示例代码对不上的地方。说明文档上大量出现wxpy.Bot(),wxpy.Chat(),wxpy.User(),wxpy.Friend等等 2、相关类的说明 运行上述代码,微信扫描二维码登陆(新注册的微信登陆不了) VScode终端调试界面会出现: Login successfully as xxxx (xxxx指你的微信名) In[1]: 可以在In[1]: 命令行界面输入调试代码。如:

mf = bot.friends()
print(mf)

调试界面会输出你的朋友列表。 在命令行界面输入调试代码: print(type(mf)) 会输出:

<class 'wxpy.api.chats.chats.Chats'>

在wxpy说明文档里搜索Chats 我们可以找到: class wxpy.Chats(chat_list =None, source=None) 这个类里面有search,stats,stats_text,add_all等方法 那么mf就可以调用这些方法。 在命令行继续输入: mf.stats() 调试界面会输出你朋友列表的各属性的分布情况。 在命令行输入: print(type(mf[1])) 会输出:

<class 'wxpy.api.chats.friend.Friend'>

这里我们可以知道mf的类型是个wxpy.Chats的类,只不过在调试界面中以列表的形式输出了,mf中的每个元素的类型又是 wxpy.Friend()的类。在wxpy说明文档中搜索Friend,可以找到class wxpy.Friend(raw, bot)的说明。发现帮助文档只是几个字“好友对象”。此时点击源代码,跳到Friend类的源代码,发现源代码为:

# coding: utf-8
from __future__ import unicode_literals
import logging
from .user import User
logger = logging.getLogger(__name__)
class Friend(User):
"""
    好友对象
    """
    pass

这里知道Friend类继承了类User。在说明文档中搜索User,找到wxpy.User()的说明。 说明里有这样的一句话“好友(Friend)、群聊成员(Member),和公众号(MP) 的基础类”。这就说明基础类User的属性和方法,类Friend也是可以用的。 在命令行输入: mf[1].province 结果输出好友的所在省份。

二、关于@bot.register() 这里@是什么鬼?@是函数装饰器操作符。那什么是函数装饰器?对我这种小白很困惑。在理解这问题之前先来理解下内嵌函数和闭包 1、先来看内嵌函数

def fun1():
    print("fun1()正在被调用...")
    def fun2():
        print("fun2()正在被调用...")
    return fun2() #或者fun2() 运行结果一样

fun1()

运行结果:

fun1()正在被调用... fun2()正在被调用...

这里return 的是fun2()。是函数fun2的调用 2、下面看return fun2会发生什么

def fun1():
    print("fun1()正在被调用...")
    def fun2():
        print("fun2()正在被调用...")
    return fun2 

fun1()

输出结果是:

fun1()正在被调用…

改一下,运行: fun1()() 输出结果是:

fun1()正在被调用… fun2()正在被调用…

这里的fun1()相当于是fun2了。也就是说内嵌函数如果return 自身函数名,那么外层函数的调用 fun1(),就是内嵌函数本身。注意:fun1()()不等于直接调用fun2()。因为print("fun1()正在被调用...")是绕不过去的 3、下面看函数本身作为参数传递给其他函数

def fun1(func):
    print("fun1()正在被调用...")
    def fun2():
        print("fun2()正在被调用...")
        func()
    return fun2 

def fun_test():
    print("fun_test()正在被调用...")
####  part1  ######
fun1(fun_test)()
####  part2  ######
fun_another = fun1(fun_test)
fun_another()
####  part3  ######
fun_test = fun1(fun_test)
fun_test()

part1和part2和part3是完全一样的。 输出的结果都是

fun1()正在被调用... fun2()正在被调用... fun_test()正在被调用.

.. 注意part3让人很难理解,换成part2方式就好理解了 4、闭包 如果在一个内部函数里,对在外部作用域但不是全局作用域的变量进行引用(简而言之:就是在嵌套函数的环境下,内部函数引用了外部函数的局部变量),那么内部函数就被认为是闭包。这里fun2()就是个闭包

5、装饰器

def fun1(func):
    print("fun1()正在被调用...")
    def fun2():
        print("fun2()正在被调用...")
        func()
return fun2

@fun1
def fun_test():
    print("fun_test()正在被调用...")

fun_test()

输出结果:

fun1()正在被调用... fun2()正在被调用... fun_test()正在被调用...

这里看到没有,这里的代码和上一段的代码效果是一样的,只是不用多加括号了。fun1(fun_test)()看着多少还是有点突兀的。装饰器操作符@的应用可以简化代码,使其更容易读。 这里就比较好理解“装饰”这个词了。没有装饰时,调用fun_test()输出的是 “fun_test()正在被调用...” 但是被@fun1装饰后,再调用fun_test() 输出的是: fun1()正在被调用... fun2()正在被调用... fun_test()正在被调用... 如果只讲到这里,用上面这个简单的例子来说明装饰器的作用的话,估计还是不能理解为什么要用装饰器。那就回到@bot.register()

6、装饰器的作用 在wxpy里有这样的一段示例代码:

from wxpy import *
bot = Bot()
@bot.register()
def print_messages(msg):
    print(msg)
# 堵塞线程,并进入 Python 命令行
embed()

如果用VScode运行(已经安装过wxpy库),登陆微信,那么你微信上所有接收到的信息都会被打印出来。 一开始我对print_messages(msg)中的这个msg参数理解不了,感觉很突兀。 我们来看一下有关Bot.register()的说明文档。 image

这里的注册规则指的是bot.register()的参数,起到一定的过滤作用,这里没有参数,默认接收所有信息。执行函数是指print_messages(msg)。消息对象作为唯一参数传递给msg。 那么问题又来了,既然消息对象作为唯一参数传递给msg,为什么不写成常规的好理解的 print_messages(bot.register())呢?而是要费那么多劲,而且写成不好理解的:

@bot.register()
def print_messages(msg):
    print(msg)

再说了,上面的这三句代码,其实和下面的是一样的(参考前面的代码举例):

def print_messages(msg):
    print(msg)
bot.register()(print_messages)

这里反而是bot.register()(print_messages) 而不是print_messages(bot.register())为什么呢? 这里就得体会装饰器的作用了。举个生活中的例子,去饭店吃饭,饭店给你看菜单,你只要带嘴和钱就行了。这里的菜单可以理解为消息对象传递给msg的数据。饭店可以理解为被装饰好了的数据环境也就是@bot.register(),里面有厨师,服务员,餐桌,饭菜,茶水等。你进了这个饭店按照它的规则,你就可以支配里面的资源,比如叫个服务员,让厨师帮你炒什么菜,喝什么果汁等等。你想吃鱼、吃肉、吃辣、吃甜,消费多少这些可以理解为每个人print_messages()函数的个性化操作。 这里可能就好理解了,个人到饭店里,饭店里提供的不仅仅是饭菜,还有各种服务,端茶倒水等等。对于bot.register()(print_messages)而言,在函数print_messages内部是可以用bot.register()的属性和方法的。而print_messages(bot.register())虽然把消息对象传递给了print_messages,但是对数据的处理方法,是要自己另外写代码完成的。就好比,你到了饭店把菜单拿了,怎么做,你回家自己来,放着饭店的厨师,服务员,桌椅,蔬菜鱼肉不用一样!

EthanYan6 commented 5 years ago

哎呦,不错哦,点个赞