Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

力扣的程序是如何运行的 #205

Open
yihong0618 opened this issue Feb 1, 2021 · 4 comments
Open

力扣的程序是如何运行的 #205

yihong0618 opened this issue Feb 1, 2021 · 4 comments
Labels
Top Top label of gitblog 技术文章 技术文章

Comments

@yihong0618
Copy link
Owner

yihong0618 commented Feb 1, 2021

初:

一般刚大家刷 LeetCode 难免好奇为什么会自动生成个 class Solution: 点击提交后台就能直接运行。虽然我们不能拿到 LeetCode 的源码,但是经过初步尝试,我发现,我们是能通过一定手段搞清楚 LeetCode 是如何提交运行的。

image

起因:

某天一位群友发了个问题,他有个地方 typo 把小写的 l 写成大写的 L, LeetCode 竟然能编译通过,代码如下:

class Solution:
    def threeSum(self, nums: List[int]) -> List[List[int]]:
        n = len(nums)
        res = []
        if (not nums or n <3):
            return []
        for i in range(n):
            if nums[i] > 0:
                return res
            if i > 0 and nums[i] == [i-1]:
                continue
            l = i + 1
            r = n - 1
            while l < r:
                if (nums[i] + nums[l] + nums[r]) == 0:
                    res.append([nums[i], nums[l], nums[r]])
                    while L < r and nums[l] == nums[l+1]:  # 此处写成了大写的 L
                        l = l + 1
                    while L < r and nums[r] = nums[r-1]:
                        r = r-1
                    l = l + 1
                    r = r - 1
                elif (nums[i] + nums[l] + nums[r]) > 0:
                    r = r - 1
                else:
                    l = l + 1
        return res

接下来有人回复,是因为 LeetCode 从 re 中 import * 导致了 L 是个全局变量,这个 L 的值是 4. 证明如下图:
image

可见,这个 L 是 re 里的 re.LOCALE
image

探索

那么我们就可以好奇下,LeetCode 都导入了哪些默认的包呢。有哪些是 import * 呢 ?

直接 print globals() 发现是 copyright, 至于为啥是这个 copyright, 好奇的同学可以查一下,挺有趣的。
image
于是尝试下 print globals().keys(), 嗯,LeetCode 默认导入了相当多的模块,很多是 import *, Python 解析速度慢,也不奇怪了。
image
再尝试下用 sys 模块打印 import
image

知道这个之后,大家刷题可以不用在 collections 里导入包了

继续探索

那么,既然他们好多模块是默认导入的,比如比较危险的 sys 和 os 模块,那么我们是否能做点什么呢?

  1. 尝试看源码,简单粗暴直接 listdir, 发现当前文件夹里有三个文件,而 / 中有 leetcodedockerenv 和猜想的一样,LeetCode 是用 docker 运行程序的
    image
  2. 那么我们看看这个 precompiled 有什么呢,发现了一些 .pyc 文件,这个后续再用
  3. 继续 listdir 找我们需要的,最后在 /mnt 找到了 .py 文件,我们看看是啥。

很好,破案了。
image

具体代码如下: 原来 LeetCode 看似神秘的程序运行构造也并不复杂。

# coding: utf-8
from string import *
from re import *
from datetime import *
from collections import *
from heapq import *
from bisect import *
from copy import *
from math import *
from random import *
from statistics import *
from itertools import *
from functools import *
from operator import *
from io import *
from sys import *
from json import *
from builtins import *

import string
import re
import datetime
import collections
import heapq
import bisect
import copy
import math
import random
import statistics
import itertools
import functools
import operator
import io
import sys
import json

import precompiled.__settings__
from precompiled.__deserializer__ import __Deserializer__
from precompiled.__deserializer__ import DeserializeError
from precompiled.__serializer__ import __Serializer__
from precompiled.__utils__ import __Utils__
from precompiled.listnode import ListNode
from precompiled.nestedinteger import NestedInteger
from precompiled.treenode import TreeNode

from typing import *

sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')

# user submitted code insert below
# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def addTwoNumbers(self, l1: ListNode, l2: ListNode) -> ListNode:
        print(os.listdir("/mnt"))
        with open("/mnt/prog_joined.py") as f:
            print(f.read())
import sys
import os
import ujson as json

def _driver():

    des = __Deserializer__()
    ser = __Serializer__()
    SEPARATOR = "\x1b\x09\x1d"
    f = open("user.out", "wb", 0)
    lines = __Utils__().read_lines()

    while True:
        line = next(lines, None)
        if line == None:
            break
        param_1 = des._deserialize(line, 'ListNode')
        
        line = next(lines, None)
        if line == None:
            raise Exception("Testcase does not have enough input arguments. Expected argument 'l2'")
        param_2 = des._deserialize(line, 'ListNode')
        
        ret = Solution().addTwoNumbers(param_1, param_2)
        try:
            out = ser._serialize(ret, 'ListNode')
        except:
            raise TypeError(str(ret) + " is not valid value for the expected return type ListNode");
        out = str.encode(out + '\n')
        f.write(out)
        sys.stdout.write(SEPARATOR)


if __name__ == '__main__':
    _driver()

至于下图中的这些是什么?LeetCode 其实在里面放的是 pyc 文件,但是大部分 pyc 是能转换回来的,通过一些手段,这个留给大家感兴趣自己研究哈哈。
image

后续

理论上其它语言可以用同样的思路。至于其它好玩的事情,大家可以自行发掘(笑)。

@yihong0618 yihong0618 added 技术文章 技术文章 Top Top label of gitblog labels Feb 1, 2021
@chaleaoch
Copy link

大佬特质呼呼往外冒.

@yihong0618 yihong0618 changed the title LeetCode 的 Python 程序是如何运行的。 LeetCode 的 Python 程序是如何运行的 Feb 1, 2021
@yihong0618 yihong0618 changed the title LeetCode 的 Python 程序是如何运行的 力扣的程序是如何运行的 Feb 3, 2021
@xuxucode
Copy link

xuxucode commented Jan 6, 2022

牛PI

@coolp4n
Copy link

coolp4n commented Sep 15, 2023

cool

@bestcondition
Copy link

🐂🍺

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Top Top label of gitblog 技术文章 技术文章
Projects
None yet
Development

No branches or pull requests

5 participants