好友
阅读权限25
听众
最后登录1970-1-1
|
本帖最后由 hrh123 于 2023-6-30 18:05 编辑
print("Hello, World!") ,恐怕是每个人学Python的第一个程序,这几天心血来潮,看到Hello World就让我想到我初学编程时的样貌,于是打算简单地给它"重构"下,先亮代码:
import re
import tkinter as tk
import asyncio
class HelloWorldGUI:
def __init__(self, master):
self.master = master
self.label = tk.Label(self.master)
self.label.pack()
self.hello_world_generator = self.hello_world()
def hello_world(self):
while True:
yield "Hello, World!"
def update_label(self):
try:
message = next(self.hello_world_generator)
except StopIteration:
self.hello_world_generator = self.hello_world()
message = next(self.hello_world_generator)
self.label["text"] = message
def start(self):
self.master.after(0, self.update_label)
self.master.mainloop()
def coroutine_decorator(func):
def wrapper(*args, **kwargs):
coro = func(*args, **kwargs)
next(coro)
return coro
return wrapper
@coroutine_decorator
def regex_coroutine(regex, target):
while True:
text = yield
matches = re.findall(regex, text)
for match in matches:
target.send(match)
def main():
root = tk.Tk()
gui = HelloWorldGUI(root)
regex = r"Hello, World!"
@coroutine_decorator
def print_match():
while True:
match = yield
print(f"Match found: '{match}'")
regex_coro = regex_coroutine(regex, print_match())
loop = asyncio.get_event_loop()
def schedule_regex_coroutine():
text = gui.label["text"]
regex_coro.send(text)
loop.call_later(1, schedule_regex_coroutine)
loop.call_soon(schedule_regex_coroutine)
gui.start()
if __name__ == "__main__":
main()
现在来详细分析下具体原理(结合代码看效果更佳):
- 导入必要的库,不多说了
- 定义一个HelloWorldGUI类,用来封装GUI
- 在类的初始化方法中,创建一个master属性,用于存储tkinter的根窗口对象;创建一个label属性,用于存储tkinter的标签对象,并将其添加到master中;创建一个hello_world_generator属性,用于存储一个生成器对象,该生成器可以不断产生“Hello, World!”字符串
- 定义一个hello_world方法,该方法使用一个while循环.不断地yield“Hello,World!”字符串,用于创建上述生成器对象
- 定义一个update_label方法,用于更新label的文本内容.该方法使用try-except语句,尝试从hello_world_generator中获取下一个字符串,并赋值给message变量.如果遇到StopIteration异常.说明生成器已经耗尽,那么重新创建一个新的生成器对象.并从中获取下一个字符串.然后将message变量赋值给label的text属性
- 定义一个start方法,用于启动GUI的主循环.该方法使用master的after方法,在0毫秒后调用update_label方法;然后使用master的mainloop方法,进入GUI的事件循环
- 定义一个coroutine_decorator函数,用于装饰协程函数.该函数接受一个函数作为参数,并返回一个包装函数.包装函数会调用原函数,并获取返回的协程对象.然后使用next函数预激活协程对象,并返回它
- 使用coroutine_decorator装饰一个regex_coroutine函数,用于创建一个协程对象,该协程可以接收文本,并使用正则表达式查找匹配结果,并将其发送给另一个目标协程.该函数接受两个参数:regex和target.regex是一个正则表达式字符串,target是另一个协程对象.该函数使用一个while循环,不断地yield等待接收文本,并使用re模块的findall函数查找匹配结果.然后使用for循环遍历匹配结果,并将其发送给target协程
- 定义一个main函数,用于执行主要逻辑
- 在main函数中,创建一个tkinter的根窗口对象,并赋值给root变量;创建一个HelloWorldGUI对象,并传入root作为参数,并赋值给gui变量
- 定义一个regex变量,赋值为r"Hello, World!",表示要查找的正则表达式
- 使用coroutine_decorator装饰一个print_match函数,用于创建一个协程对象,该协程可以接收匹配结果,并打印出来.该函数使用一个while循环,不断地yield等待接收匹配结果,并赋值给match变量.然后使用print函数打印出匹配结果
- 创建一个print_match协程对象,并赋值给print_match变量;创建一个regex_coroutine协程对象,并传入regex和print_match作为参数并赋值给regex_coro变量
- 使用asyncio模块的get_event_loop函数获取当前事件循环对象,并赋值给loop变量
- 定义一个schedule_regex_coroutine函数,用于定时调度regex_coro协程.该函数首先获取label的text属性,并赋值给text变量;然后使用regex_coro的send方法发送text给协程;最后使用loop的call_later方法,在1秒后再次调用schedule_regex_coroutine函数
- 使用loop的call_soon方法,尽快调用schedule_regex_coroutine函数
- 使用gui的start方法,启动GUI的主循环
- 代码的最后,使用if语句判断是否为主模块,如果是,则调用main函数
最终效果:生成一个GUI,中间有一个标签,显示Hello, World!的文本
更新:相信大家已经学会了上面的基本代码,现在继续来上强度:
import asyncio
import re
import tkinter as tk
class MyDescriptor:
def __get__(self, obj, objtype):
print(f"Getting attribute '{self.name}'")
return obj.__dict__[self.name]
def __set__(self, obj, value):
print(f"Setting attribute '{self.name}' to '{value}'")
obj.__dict__[self.name] = value
def __set_name__(self, owner, name):
self.name = name
class MyMeta(type):
def __new__(cls, name, bases, attrs):
print(f"Creating class '{name}' with bases '{bases}' and attributes '{attrs}'")
new_attrs = {}
for attr_name, attr_value in attrs.items():
if isinstance(attr_value, MyDescriptor):
attr_value.name = attr_name
new_attrs[attr_name] = attr_value
return super().__new__(cls, name, bases, new_attrs)
class HelloWorldGUI(metaclass=MyMeta):
label = MyDescriptor()
def __init__(self, master):
self.master = master
self.label = tk.Label(self.master)
self.label.pack()
self.hello_world_generator = self.hello_world()
def hello_world(self):
while True:
yield "Hello, World!"
def update_label(self):
try:
message = next(self.hello_world_generator)
except StopIteration:
self.hello_world_generator = self.hello_world()
message = next(self.hello_world_generator)
self.label["text"] = message
def start(self):
self.master.after(0, self.update_label)
self.master.mainloop()
def coroutine_decorator(func):
def wrapper(*args, **kwargs):
coro = func(*args, **kwargs)
next(coro)
return coro
return wrapper
@coroutine_decorator
def regex_coroutine(regex, target):
while True:
text = yield
matches = re.findall(regex, text)
for match in matches:
target.send(match)
def main():
root = tk.Tk()
gui = HelloWorldGUI(root)
regex = r"Hello, World!"
@coroutine_decorator
def print_match():
while True:
match = yield
print(f"Match found: '{match}'")
regex_coro = regex_coroutine(regex, print_match())
loop = asyncio.get_event_loop()
def schedule_regex_coroutine():
text = gui.label["text"]
regex_coro.send(text)
loop.call_later(1, schedule_regex_coroutine)
loop.call_soon(schedule_regex_coroutine)
gui.start()
if __name__ == "__main__":
main()
代码逻辑分析:
- 定义了一个自定义描述符类
MyDescriptor ,它重写了__get__ ,__set__ 和__set_name__ 方法,用于在获取或设置属性时打印一些信息,并将属性名和值存储在对象的__dict__ 中
- 定义了一个自定义元类
MyMeta ,它重写了__new__ 方法,用于在创建类时打印一些信息,并将描述符对象的名字设置为属性名
- 定义了一个GUI类
HelloWorldGUI ,它使用了自定义元类,并定义了一个描述符属性label .它的初始化方法中,创建了一个tkinter标签对象,并将其赋值给描述符属性.它还定义了一个生成器方法hello_world ,用于不断地生成"Hello,World!"字符串.它还定义了一个更新标签文本的方法update_label ,用于从生成器中获取下一个字符串,并将其设置为标签的文本.最后,它定义了一个启动GUI的方法start ,用于调用tkinter的定时器和主循环
- 定义了一个协程装饰器函数
coroutine_decorator ,用于在调用协程函数时自动执行一次next操作,以便激活协程
- 定义了一个正则匹配协程函数
regex_coroutine ,它接受一个正则表达式和一个目标协程作为参数.它不断地从外部接收文本,并使用正则表达式在文本中查找匹配结果.对于每个匹配结果,它将其发送给目标协程
- 在主函数中,创建了一个tkinter根窗口对象,并传递给GUI类的实例.然后定义了一个正则表达式字符串"Hello, World!",用于匹配"Hello, World!"中的"Hello, World!".然后定义了一个打印匹配结果的协程函数
print_match ,并使用协程装饰器装饰.然后创建了一个正则匹配协程对象,并将正则表达式和打印匹配结果的协程作为参数传递.然后获取了asyncio模块的事件循环对象,并定义了一个调度正则匹配协程的函数schedule_regex_coroutine .这个函数会获取当前标签的文本,并将其发送给正则匹配协程.然后使用事件循环的定时器,在一秒后再次调用这个函数.最后,使用事件循环的启动函数,在尽快执行一次调度正则匹配协程的函数之后,启动GUI
最终效果:依然是生成一个GUI,中间有一个标签,显示Hello, World!的文本
|
免费评分
-
查看全部评分
|