hrh123 发表于 2023-6-29 17:11

(整活向)史上最复杂的Hello Python World!

本帖最后由 hrh123 于 2023-6-30 18:05 编辑

`print("Hello, World!")`,恐怕是每个人学Python的第一个程序,这几天心血来潮,看到HelloWorld就让我想到我初学编程时的样貌,于是打算简单地给它"重构"下,先亮代码:
```python
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()
```
现在来详细分析下具体原理(结合代码看效果更佳):
1. 导入必要的库,不多说了
2. 定义一个HelloWorldGUI类,用来封装GUI
3. 在类的初始化方法中,创建一个master属性,用于存储tkinter的根窗口对象;创建一个label属性,用于存储tkinter的标签对象,并将其添加到master中;创建一个hello_world_generator属性,用于存储一个生成器对象,该生成器可以不断产生“Hello, World!”字符串
4. 定义一个hello_world方法,该方法使用一个while循环.不断地yield“Hello,World!”字符串,用于创建上述生成器对象
5. 定义一个update_label方法,用于更新label的文本内容.该方法使用try-except语句,尝试从hello_world_generator中获取下一个字符串,并赋值给message变量.如果遇到StopIteration异常.说明生成器已经耗尽,那么重新创建一个新的生成器对象.并从中获取下一个字符串.然后将message变量赋值给label的text属性
6. 定义一个start方法,用于启动GUI的主循环.该方法使用master的after方法,在0毫秒后调用update_label方法;然后使用master的mainloop方法,进入GUI的事件循环
7. 定义一个coroutine_decorator函数,用于装饰协程函数.该函数接受一个函数作为参数,并返回一个包装函数.包装函数会调用原函数,并获取返回的协程对象.然后使用next函数预激活协程对象,并返回它
8. 使用coroutine_decorator装饰一个regex_coroutine函数,用于创建一个协程对象,该协程可以接收文本,并使用正则表达式查找匹配结果,并将其发送给另一个目标协程.该函数接受两个参数:regex和target.regex是一个正则表达式字符串,target是另一个协程对象.该函数使用一个while循环,不断地yield等待接收文本,并使用re模块的findall函数查找匹配结果.然后使用for循环遍历匹配结果,并将其发送给target协程
9. 定义一个main函数,用于执行主要逻辑
10. 在main函数中,创建一个tkinter的根窗口对象,并赋值给root变量;创建一个HelloWorldGUI对象,并传入root作为参数,并赋值给gui变量
11. 定义一个regex变量,赋值为r"Hello, World!",表示要查找的正则表达式
12. 使用coroutine_decorator装饰一个print_match函数,用于创建一个协程对象,该协程可以接收匹配结果,并打印出来.该函数使用一个while循环,不断地yield等待接收匹配结果,并赋值给match变量.然后使用print函数打印出匹配结果
13. 创建一个print_match协程对象,并赋值给print_match变量;创建一个regex_coroutine协程对象,并传入regex和print_match作为参数并赋值给regex_coro变量
14. 使用asyncio模块的get_event_loop函数获取当前事件循环对象,并赋值给loop变量
15. 定义一个schedule_regex_coroutine函数,用于定时调度regex_coro协程.该函数首先获取label的text属性,并赋值给text变量;然后使用regex_coro的send方法发送text给协程;最后使用loop的call_later方法,在1秒后再次调用schedule_regex_coroutine函数
16. 使用loop的call_soon方法,尽快调用schedule_regex_coroutine函数
17. 使用gui的start方法,启动GUI的主循环
18. 代码的最后,使用if语句判断是否为主模块,如果是,则调用main函数

最终效果:生成一个GUI,中间有一个标签,显示Hello, World!的文本
* * *
更新:相信大家已经学会了上面的基本代码,现在继续来上强度:
```python
import asyncio
import re
import tkinter as tk


class MyDescriptor:
    def __get__(self, obj, objtype):
      print(f"Getting attribute '{self.name}'")
      return obj.__dict__

    def __set__(self, obj, value):
      print(f"Setting attribute '{self.name}' to '{value}'")
      obj.__dict__ = 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_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()
```
代码逻辑分析:
1. 定义了一个自定义描述符类`MyDescriptor`,它重写了`__get__`,`__set__`和`__set_name__`方法,用于在获取或设置属性时打印一些信息,并将属性名和值存储在对象的`__dict__`中
2. 定义了一个自定义元类`MyMeta`,它重写了`__new__`方法,用于在创建类时打印一些信息,并将描述符对象的名字设置为属性名
3. 定义了一个GUI类`HelloWorldGUI`,它使用了自定义元类,并定义了一个描述符属性`label`.它的初始化方法中,创建了一个tkinter标签对象,并将其赋值给描述符属性.它还定义了一个生成器方法`hello_world`,用于不断地生成"Hello,World!"字符串.它还定义了一个更新标签文本的方法`update_label`,用于从生成器中获取下一个字符串,并将其设置为标签的文本.最后,它定义了一个启动GUI的方法`start`,用于调用tkinter的定时器和主循环
4. 定义了一个协程装饰器函数`coroutine_decorator`,用于在调用协程函数时自动执行一次next操作,以便激活协程
5. 定义了一个正则匹配协程函数`regex_coroutine`,它接受一个正则表达式和一个目标协程作为参数.它不断地从外部接收文本,并使用正则表达式在文本中查找匹配结果.对于每个匹配结果,它将其发送给目标协程
6. 在主函数中,创建了一个tkinter根窗口对象,并传递给GUI类的实例.然后定义了一个正则表达式字符串"Hello, World!",用于匹配"Hello, World!"中的"Hello, World!".然后定义了一个打印匹配结果的协程函数`print_match`,并使用协程装饰器装饰.然后创建了一个正则匹配协程对象,并将正则表达式和打印匹配结果的协程作为参数传递.然后获取了asyncio模块的事件循环对象,并定义了一个调度正则匹配协程的函数`schedule_regex_coroutine`.这个函数会获取当前标签的文本,并将其发送给正则匹配协程.然后使用事件循环的定时器,在一秒后再次调用这个函数.最后,使用事件循环的启动函数,在尽快执行一次调度正则匹配协程的函数之后,启动GUI

最终效果:依然是生成一个GUI,中间有一个标签,显示Hello, World!的文本

hrh123 发表于 2023-6-30 17:49

本帖最后由 hrh123 于 2023-6-30 18:06 编辑

最终版代码,这次就不分析了,感兴趣的自己看看:
```python
import asyncio
import re
import tkinter as tk


class MyDescriptor:
    def __get__(self, obj, objtype):
      print(f"Getting attribute '{self.name}'")
      return obj.__dict__

    def __set__(self, obj, value):
      print(f"Setting attribute '{self.name}' to '{value}'")
      obj.__dict__ = 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_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()


class coroutine_decorator:
    def __init__(self, func):
      self.func = func

    def __call__(self, *args, **kwargs):
      coro = self.func(*args, **kwargs)
      next(coro)
      return coro


def find_matches(text, regex):
    return re.finditer(regex, text)


def regex_coroutine(regex, target):
    while True:
      text = yield
      matches = find_matches(text, regex)
      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"]
      for match in regex_coro:
            regex_coro.send(text)
      loop.call_later(1, schedule_regex_coroutine)

    loop.call_soon(schedule_regex_coroutine)

    gui.start()


if __name__ == "__main__":
    main()
```

dreamrise 发表于 2023-6-30 08:58

直接复制粘贴了一个行内注释代码版
```
# coding:utf-8
"""最终效果:生成一个GUI,中间有一个标签,显示Hello,World!的文本
"""
import re
import tkinter as tk
import asyncio


class HelloWorldGUI:
    """定义一个HelloWorldGUI类,用来封装GUI"""
    def __init__(self, master):
      """
      在类的初始化方法中,创建一个master属性,用于存储tkinter的根窗口对象;
      创建一个label属性,用于存储tkinter的标签对象,并将其添加到master中;
      创建一个hello_world_generator属性,用于存储一个生成器对象,该生成器可以不断产生“Hello,World!”字符串
      """
      self.master = master
      self.label = tk.Label(self.master)
      self.label.pack()
      self.hello_world_generator = self.hello_world()

    def hello_world(self):
      """定义一个hello_world方法,该方法使用一个while循环.不断地yield“Hello,World!”字符串,用于创建上述生成器对象"""
      while True:
            yield "Hello, World!"

    def update_label(self):
      """定义一个update_label方法,用于更新label的文本内容.
      该方法使用try-except语句,尝试从hello_world_generator中获取下一个字符串,并赋值给message变量.
      如果遇到StopIteration异常.说明生成器已经耗尽,
      那么重新创建一个新的生成器对象.并从中获取下一个字符串.然后将message变量赋值给label的text属性
      """
      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):
      """定义一个start方法,用于启动GUI的主循环.该方法使用master的after方法,在0毫秒后调用update_label方法;
      然后使用master的mainloop方法,进入GUI的事件循环"""
      self.master.after(0, self.update_label)
      self.master.mainloop()


def coroutine_decorator(func):
    """
    定义一个coroutine_decorator函数,用于装饰协程函数.该函数接受一个函数作为参数,并返回一个包装函数.
    包装函数会调用原函数,并获取返回的协程对象.然后使用next函数预激活协程对象,并返回它
    """
    def wrapper(*args, **kwargs):
      coro = func(*args, **kwargs)
      next(coro)
      return coro

    return wrapper


@coroutine_decorator
def regex_coroutine(regex, target):
    """使用coroutine_decorator装饰一个regex_coroutine函数,用于创建一个协程对象,
    该协程可以接收文本,并使用正则表达式查找匹配结果,并将其发送给另一个目标协程.
    该函数接受两个参数:regex和target.
    regex是一个正则表达式字符串,target是另一个协程对象.该函数使用一个while循环,不断地yield等待接收文本,并使用re模块的findall函数查找匹配结果.然后使用for循环遍历匹配结果,并将其发送给target协程
    """
    while True:
      text = yield
      matches = re.findall(regex, text)
      for match in matches:
            target.send(match)


def main():
    """定义一个main函数,用于执行主要逻辑"""
    root = tk.Tk()
    """在main函数中,创建一个tkinter的根窗口对象,并赋值给root变量;"""
   
    gui = HelloWorldGUI(root)
    """创建一个HelloWorldGUI对象,并传入root作为参数,并赋值给gui变量"""

    regex = r"lo"
    """定义一个regex变量,赋值为r"lo",表示要查找的正则表达式"""

    @coroutine_decorator
    def print_match():
      """使用coroutine_decorator装饰一个print_match函数,用于创建一个协程对象,
      该协程可以接收匹配结果,并打印出来.该函数使用一个while循环,不断地yield等待接收匹配结果,并赋值给match变量.然后使用print函数打印出匹配结果"""
      while True:
            match = yield
            print(f"Match found: '{match}'")

    regex_coro = regex_coroutine(regex, print_match())
    """创建一个print_match协程对象,并赋值给print_match变量;创建一个regex_coroutine协程对象,并传入regex和print_match作为参数并赋值给regex_coro变量"""
    loop = asyncio.get_event_loop()
    """使用asyncio模块的get_event_loop函数获取当前事件循环对象,并赋值给loop变量"""
   

    def schedule_regex_coroutine():
      """
      定义一个schedule_regex_coroutine函数,用于定时调度regex_coro协程.
      该函数首先获取label的text属性,并赋值给text变量;
      然后使用regex_coro的send方法发送text给协程;
      最后使用loop的call_later方法,在1秒后再次调用schedule_regex_coroutine函数
      """
      text = gui.label["text"]
      regex_coro.send(text)
      loop.call_later(1, schedule_regex_coroutine)

    loop.call_soon(schedule_regex_coroutine)
    """使用loop的call_soon方法,尽快调用schedule_regex_coroutine函数"""

    gui.start()
    """使用gui的start方法,启动GUI的主循环"""


if __name__ == "__main__":
    """代码的最后,使用if语句判断是否为主模块,如果是,则调用main函数"""
    main()

```

px307 发表于 2023-6-29 17:22

向大佬学习

dazhaxie 发表于 2023-6-29 18:04

狠人,事件循环用select或者epoll自己写一个{:1_918:}

py学徒 发表于 2023-6-29 18:07

asyncio 是 Python 标准库中提供的一个异步编程框架。它允许您使用协程编写并发代码,通过多路复用 I/O 访问和运行网络客户端和服务器。

通过使用 asyncio,您可以编写比传统同步编程更高效和可扩展的异步代码。它使用事件循环来管理多个任务的并发执行,使您能够异步执行 I/O 操作,而不会阻塞其他任务的执行。

要使用 asyncio,通常需要使用 async 关键字将函数定义为协程,在其中等待异步操作。事件循环负责管理这些协程,安排它们的执行,并在它们等待 I/O 或其他非阻塞操作时进行切换。

moruye 发表于 2023-6-29 18:09

myconan 发表于 2023-6-29 18:48

牛人教我学编程

諦覠 发表于 2023-6-29 18:55

第一次见到python版的复杂hello world

RainPPR 发表于 2023-6-29 21:59

记得第一次用Python编的“Hello World”是:
printf("Hello World")
运行:NameError: name 'printf' is not defined
{:301_1001:}

jjoobb123 发表于 2023-6-30 08:35

有点复杂,膜拜下

vethenc 发表于 2023-6-30 08:46

大佬,带带我
页: [1] 2 3
查看完整版本: (整活向)史上最复杂的Hello Python World!