zoukankan      html  css  js  c++  java
  • 从Python的exec()聊起

    exec()是Python的built-in函数。其作用很好描述,就是执行以string类型存储的Python代码。话不多说举个例子。

    >>> i = 2
    >>> j = 3
    >>> exec("ans = i + j")
    >>> print("Answer is: ", ans)
    Answer is:  5
    >>>
    

    在上个例子里面,ans变量并没有显式的定义,但仍然可以在print函数中调用。这是exec语句执行了"ans = i + j"中的代码,定义了ans变量。

    乍一看,这个功能很像C语言里的define宏定义:都是在代码里面插入可变的代码段。但其实还不一样,再看一个例子。

    >>> i = 5
    >>> j = 7
    >>> n = 0
    >>> while n < i:
    ...     print("looping")
    ...     if j > i:
    ...         break
    ...     n += 1
    ... 
    looping
    >>> 
    

    假设使用exec函数。

    >>> i = 5
    >>> j = 7
    >>> n = 0
    >>> while n < i:
    ...     print("looping")
    ...     exec("""if j > 5:
    ...           
        break""")
    ...     n += 1
    ... 
    looping
    Traceback (most recent call last):
      File "<stdin>", line 4, in <module>
      File "<string>", line 3
    SyntaxError: "break" outside loop
    

    在这里,exec函数为什么失效了呢?

    根据Python文档,解释器会在执行到break语句时,会跳出离该句最近的while、for循环,如果解释器无法找到while、for循环,就会报错。因此,此处报错,说明了Python解释器没有找到exec之前的while循环。

    实际上,仔细看文档会发现,解释器遇到exec函数时,会独立执行字符串内的语句。如果还有传参,那都是定义变量的字典。解释器,不会寻找字符串外的语法结构。也就是说,在这个例子中,解释器会独立执行语句

    if j > i:
        break
    

    难怪,解释器会报错了。

    而C语言完全不存在这个问题。

    #include <stdio.h>
    #define JUDGE(x, y) if ((x) > (y))
        break;
    int main() {
        int i = 5;
        int j = 7;
        int n = 0;
        while (n < i) {
            printf("looping
    ");
            JUDGE(j, i)
            n++;
        };
        
        return 0;
    }
    

    编译以后运行一下,看看。

    $ gcc test.c -o test 
    $ ./test 
    looping
    $
    

    两个表面看上去类似的功能背后的原理完全不同。C语言的define,会在编译的第一步——“预处理”中完成替换。编译器在后续语法分析时,完全不知道原始代码里的宏定义是什么样子。

    多说两句

    exec可以帮助完成过程抽象

    exec是一个比较偏门的函数,而且过多地使用这个函数会降低代码的可读性。

    不过,它有助于在开发过程中循序渐进的完成“过程抽象”。

    最近,工作中就遇到一个场景:解析不同语言的代码文件。代码文件大致一样,却又随着语言语法的不同而在解析细节上有着不一样。我在最初拿到这个任务时,对于如何抽象出类和对象完全没有头绪。就先对不同的代码文件,单独写一个过程函数。全部写完,单元测试跑过之后,再去对比:归纳出共有的方法,提取共同的结构作为父类的内容。用exec函数剥离代码,分离出子类的私有方法。

    C语言中实现面向对象

    C语言是典型的面向过程语言,怎么做到面向对象呢?

    这个脑洞有点大,但是大牛们已经在这么做了。之前看知乎上有人讲:真正的C语言用家,都是用宏定义来实现类似于C++的面向对象特性。当初看了也是一头雾水,这次才悟出来怎么实现。

    随便举个例子吧。

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    #define MALE 0
    #define FEMALE 1
    
    #define INIT_PERSON(Person_var, Gender, Birthday)
        struct Person* Person_var;
        Person_var = malloc(sizeof(Person_var));
        Person_var->gender = (Gender);
        strcpy(Person_var->birthday, Birthday);
    
    struct Person {
        int gender;
        char birthday[];
    };
    
    int main() {
        INIT_PERSON(li_ming, MALE, "1992-02-13")
        printf("Li ming gender is %d
    ", li_ming->gender);
        printf("Li ming birthday is %s
    ", li_ming->birthday);
    
        INIT_PERSON(han_mei_mei, FEMALE, "1989-09-21")
        printf("Han Meimei gender is %d
    ", han_mei_mei->gender);
        printf("Han Meimei birthday is %s
    ", han_mei_mei->birthday);
        
        free(li_ming);
        free(han_mei_mei);
        return 0;
    }
    

    代码中,INIT_PERSON宏就实现了:类似于面向对象中创建实例的方法。Person结构体,对应的类方法可以使用类似的方式来实现。

    父类和子类的继承呢?当然可以通过递归调用宏定义来实现了。

    当然了,以上只是一些粗浅的理解,这个方向还有很多细节可以挖掘。

  • 相关阅读:
    maven 设置日志级别
    浏览器工作原理:浅析浏览器中的页面
    浏览器工作原理:浅析浏览器中的页面
    解决uniapp的websocket连接在web和安卓正常,iOS连接不上的问题
    浏览器工作原理:浅析页面循环系统
    浏览器工作原理:浅析页面循环系统
    浏览器工作原理:浅析页面循环系统
    浅析如何使用WebSocket、SockJS、STOMP实现消息实时通讯功能:websocket/SocketJS/Stomp是什么及三者的关系、stomp协议格式、如何开启stomp、如何处理客服端发送的stomp、如何发消息给客服端、如何在任何地方发消息、如何给目标或指定用户发消息
    解决sockjs、stomp在uni-app端使用的坑
    浏览器工作原理:浅析页面循环系统
  • 原文地址:https://www.cnblogs.com/rim99/p/6980120.html
Copyright © 2011-2022 走看看