selfteaching / selfteaching/selfteaching-python-camp

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

Open
#3,764 1 comment 6 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

学习心得分享
Dominant language
Python
Stars
151
Forks
875
PR merge metrics
No merged PRs in 30d

Description

一、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,但是对数据的处理方法,是要自己另外写代码完成的。就好比,你到了饭店把菜单拿了,怎么做,你回家自己来,放着饭店的厨师,服务员,桌椅,蔬菜鱼肉不用一样!

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by reviewing the wxpy Bot, Bot.register(), and embed() examples included in the issue, then compare them with the project’s existing documentation. Done means the explanation is accurate, organized, and resolves the stated confusion about imports, classes, closures, and decorators.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
documentation
Issue type
Documentation
Difficulty
3/5
Estimated time
1-2 days
Activity status
Stale
Clarity
Needs clarification
Newbie friendliness
28/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.