• python 教程


    python 3

    >>> a = 5

    >>> b = 10

    >>> a,b = b,a

    >>> print(a,b)

    10 5

    Python 只需要一句a,b = b,a 交换两个变量的值,如果其他语言,可能需要 temp = a , a = b, b = temp 至少这三步吧,哪种语言更优雅

    基础部分

    9/3 = 2.0 除法结果是浮点数,即使两个数都是整数,结果也是浮点数

    10 // 3  = 3 结果是整数 即使除不尽

    10 %3 = 1 取余运算 结果为整数

    关于编码

    ord(‘A’) = 65

    chr(66) = ‘B’

    b’ABC’  把 str 变成 bytes,用于存储或者传输

    len(’str’) 计算及字符数

    list tuple

    List 相当于数组,可变,可以是任何数据类型 L= [ ] 或 L = list()即创建一个空的 list,list 可以包含 list,tuple也可以包含 list, 所以即使 tuple 是不可变的,其实仍然可变,不可变的只是引用.所以你想干坏事, Python 是阻止不了你的,全靠自觉.

    a = [1,2,3] b = [4,5,6]  c= a+b  c = [1,2,3,4,5,6]

    *可以复制 list 中的元素[0] * 4    [0,0,0,0]

    list 切片 可以使用指定上下限的方式来获得一个 list 的切片 list = [1,2,3,4,5,6];

    list[1:3]  [2,3] 包括后面的不包括前面的

    list.append()会自动添加到末尾

    list.insert(index,object) 添加到指定的位置

    list.pop() 出栈的方式 删除末尾的元素

    list. pop(index) 删除指定位置的元素

    list 中的元素可以是各种类型混合

    tuple 元组 ,不可变的 但是可以包含 list 作为其中的一个元素 T= () 如果只含有一个元素的 tuple,需要在这一个元素后面加”,” 消除歧义,如 tuple = (‘Bob’,)必须在单个元素后面加’,’

    其实 tuple也并不是完全不可变的, tuple里面可以包含 list, list 可变,只是 tuple 每个元素的指向不能变

    dict 相当于字典,键值对, d = {name: ‘bob’,  }  d.keys() 获取所有的 key, d.values 获取所有的 value

    d.copy 获取一个拷贝

    d.pop(key)删除一个 key 值, value 值也会被删除

    d.get(key) 查找对应 key 值得 value 如果不存在 返回 None  另外还可以d.get(key,-1) 自己指定一个值如果 key 不存在 直接返回这个值

    凡是可作用于 for 循环的对象都是 Iterable (可迭代对象)类型

    凡是可作用于 next() 函数的对象都是 Iterator(迭代器)类型,他表示一个惰性计算的序列

    集合数据类型如 list dict  str 等是 itterable 但不是 Iterator,不过可以通过 iter() 函数获得一个 Iterator 对象, Python 的 for 循环本质上就是通过不断调用 next() 函数实现的

    可以使用 iter() 函数 传入一个序列对象,即可变成可迭代对象,比如 iter = iter(list) 这个 iter 就变成了可迭代对象,如果打印它的类型可以看到<list_iterator object at 0x111ec3f60>

    可变参数

    函数如果传入的参数是可变的 可以在参数前面加 *表示参数是可变的

    关键字参数 

    def person (name,age, **kw):

    print(name,age,kw)

    函数 person 除了必选参数 name 和 age 外,还接受关键字参数 kw,这些关键字参数在函数内部自动组装为一个 dict ,在调用该函数时可以只传入必选参数,也可以传入任意个数的关键字参数

    person (‘bob’,20,city = ‘beijing’),它的作用就是可以扩展函数的功能

    生成器 generator 他是在 for 循环的过程中不断的计算出下一个元素,并在适当的条件下结束 for 循环,对于函数改成的 generator 来说遇到 return 语句或者执行到函数体的最后一句,就是结束 generator 的指令

     高阶函数

    变量可以指向函数

    函数名也是变量

    一个函数可以接收另一个函数作为参数

    map() 函数 接收两个参数,一个是函数,一个是可迭代对象list 这种, map将传入的函数依次作用于每一个 list 对象,并返回一个 list

    reduce() 函数 传入也是一个函数,一个 list, 将 list 的1,2 个元素执行 function, 然后将结果与第3个元素执行 function

    filter() 接受一个函数和一个序列,函数会依次作用于每一个元素,然后根据返回值是 true 或者 false 保留和丢弃元素,返回一个生成器,需要要 list()函数接收

    sorted() 排序高阶函数 接受一个序列和函数 

    返回函数 一个函数可以返回一个计算结果 也可以返回一个函数,返回一个函数时,牢记该函数并未执行,返回函数中不要引用可能会变化的变量

    如: def count():

    deff(j):

    def g():

    return j *j

    return g

    fs = [ ]

    for i in range(1,4):

    fs.append(f(i))

    return fs

    匿名函数关键字 lambda表示,匿名函数有个限制,就是只能有一个表达式,不用写 return, 返回值就是该表达式的结果,匿名函数也是一个函数对象,可以把匿名函数赋值给一个变量,再利用变量来调用该函数,也可以把匿名函数作为返回值返回

    偏函数 functors.partial 的作用就是,把一个函数的某些参数给固定住(也就是设置默认值),返回一个新的函数,调用这个函数会更简单.当函数的参数个数太多,需要简化时,可以使用 functools.partial 创建一个新函数,这个函数可以固定住原函数的部分参数,从而在调用时更简单

    decorator 装饰器

    不改变函数的定义在代码运行期间动态的增加功能的方式称之为装饰器

    def log(func):

    def wrapper(*args,**kw):

    print(fund.__name__)

    return func(*args,**kw)

    return wrapper 

    把@ log 放在函数的定义处 相当于执行log(function)

    多重继承MixIn python 支持多重继承机制

    class MyTCPServer (TCPServer,ForkingMixIn):

    pass

    1>类是创建实例的模板,而实例是一个一个具体的对象,各个实例拥有的数据都相互独立,互不影响;

    2>方法就是与实例绑定的函数,和普通函数不同,方法可以直接访问实例的数据

    3>通过在实例上调用方法,我们就直接操作了对象内部的数据,但无需知道方法内部的实现细节

    4> 和静态语言不同, Python 允许对实例变量绑定任何数据,也就是说,对于两个实例变量,虽然他们都是同一个类的不同实例,但拥有的变量名称都可能不同.

    初始化类和实例,实例属性,类属性

    class Person(object):

    def __init__(self,name,gender,birth):

    self.name = name

    self.gender = gender

    self.birth = birth

    注意:__ init__方法第一个参数必须是 self

    xiaoming = Person(‘xiaoming’,’Male’,’1990-1-1’)

    python 是动态语言 ,可以直接给他们的属性赋值如:xiaoming.age = 20,就添加了一个age 属性 ,但是这个属性只属于 xiaoming 这个实例对象,如 jack = Person(‘jack’,’Male’,’1992-0-0’) jack 访问 age 属性就会报错

    __name 表示私有变量 外部不可以直接访问

    类属性是直接绑定在类上的,访问类属性时不需要创建实例 实例也可以访问类属性 不能在实例上修改类属性 他并没有修改类属性 只是给实例绑定一个实例属性 实例属性的优先级比类属性高

    Python 并不是绝对安全的语言,__name 变量你也可以访问到, instance._class.__ name 即可访问到

    >>> class Student(object):

    name = 'Bob'

    def __init__(self,age):

    self.age = age

    >>> s =Student(20)

    >>> s.name

    'Bob'

    >>> s.age

    20

    >>> s.score = 100

    >>> s.score

    100

    >>> Student.number = 1

    >>> Student.number

    1

    >>> s.number

    1

    >>> s.number = 10

    >>> s.number

    10

    >>> Student.number

    1

    细心的同学可以看到,类属性,实例属性都是可以随时添加的,实例属性也能访问类属性并且赋值,但是你会发现赋值之后实例去访问类属性,值好像被修改了,然后再用类去访问类属性发现值其实没有被修改,其实你用实例去访问类属性可以访问到(如果你对实例没有该属性,否则会被覆盖,并且实例的优先级更高),但是你用实例去给类属性赋值的时候,就相当于给实例重新添加了一个新的实例属性,所以你怎么改,类属性都是不会变的,因为你改的根本不是它.

    实例方法

    实例方法就是在类中定义的函数,他的第一个参数永远是 self, 指向调用该方法的实例本身

    class Person(object):

    def __init__(self,name):

    self.__name = name

    def get_name(self):

    return self.__name

    在 class 中定义的实例方法其实也是一种属性,他实际上是一个函数对象 可以用 p1.get_grade = types.MethodType(fn_get_grade,p1,Person)把实例方法单独绑定到某一个实例上,给一个实例动态添加方法并不常见,直接在 class 中定义要更加直观

    类方法

    class Person(object):

    count = 0

    @classmethod

    def how_many(cls):

    return cls.count

    def __init__(self,name):

    self.name  = name

    Person.conut = Person.count +1

    print Person.how_many()

    p1 = Person(‘Bob’)

    print Person.how_many()

    通过标记一个@ classmethod,该方法将绑定到一个类上,类方法的第一个参数将传入类本身,因为是在类上调用,而非实例上调用,因此类方法无法获得任何实例变量,只能获得类的引用

    动态语言调用实例方法,不检查类型,只要方法存在,参数正确,就可以调用,所以 Python 的多态,传递给一个方法的不一定是子类,任何数据类型的实例都可以

    @property 装饰器 把方法装饰成属性来调用

    class Student(object):

        def __init__(self, name, score):

            self.name = name

            self.__score = score

        @property #修饰 getter 方法

        def score(self):

            return self.__score

        @score.setter# 修饰 setter 方法

        def score(self, score):

            if score < 0 or score > 100:

                raise ValueError('invalid score')

            self.__score = score

    >>> s = Student('Bob', 59)

    >>> s.score = 60

    >>> print s.score

    60

    >>> s.score = 1000

    Traceback (most recent call last):

      ...

    ValueError: invalid score

    说明对 score 赋值实际调用的是 set方法

    __slots__

    由于 Python 是动态语言,任何实例在运行期间都可以动态的添加属性,如果要限制添加的属性,比如 Student 类只许添加 name,gender,score 这3个属性,就可以用__ slots__来实现,就是指一个类允许的属性列表,其实的目的就是节约内存

    class Student (object):

    __slots__ = (‘name’,’gender’,’score’)

    def__init__(self,name,gender,score):

    self.name = name

    self.gender = gender

    self.score = score

    >>> s = Student('Bob', 'male', 59)

    >>> s.name = 'Tim' # OK

    >>> s.score = 99 # OK

    >>> s.grade = 'A'

    Traceback (most recent call last):

      ...

    AttributeError: 'Student' object has no attribute 'grade'

    __call__()

    在 Python 中任何数据类型都可以看做对象,函数也是可调用对象,一个类实例也可以变成一个可调用对象,只需实现一个__ call__()方法

    class Person(object):

    def__init__(self,name,gender):

    self.name = name

    self.gender = gender

    def __call__(self,friend):

    print’my name is %s’%self.name

    print’y friend is % s’%friend

    >>> p = Person (‘Bob’,’male’)

    >>> p(‘Tim’)

    my name is Bob

    my friend is Tim

    type()函数创建类

    要创建一个 class,type()函数依次传入3 个参数:1> class 的名称

    2> 继承的父类集合, Python 是多重继承,如果只有一个父类,别忘了 tuple的单元素写法;

    3>class 的方法名称和函数绑定,这里把函数 fn 绑定到方法名 hello 上

    如:>>> def fn(self,name=‘world’):#先定义函数

    …print (‘Hello,%s.’%name)

    >>>Hello = type(‘Hello’,(object,),dict(hello = fn)) #创建一个 Hello类

    >>> h = Hello()

    >>>h.hello()

    Hello,world.

    >>>print(type(Hello))

    <class ‘type’>

    >>>print(type(h))

    <class ‘__main__.Hello’>

    metaclass

    先定义metaclass,就可以创建类,根据类就可以创建实例,类就是 metaclass 创建出来的实例,下面通过 metaclass 给自定义的 MyList 类增加一个 add 方法:

    #attrs 就是将要创建出来的类的属性

    >>> class ListMetaclass(type):

    ...     def __new__(cls,name,bases,attrs):

    ...             attrs['add']= lambda self,value:self.append(value)

    ...             return type.__new__(cls,name,bases,attrs)

    ... 

    >>> class MyList(list,metaclass=ListMetaclass):

    ...     pass

    ... 

    >>> L = MyList()

    >>> L.add(1)

    >>> L

    [1]

    >>> L2 = list()

    >>> L2.add(1)

    Traceback (most recent call last):

      File "<stdin>", line 1, in <module>

    AttributeError: 'list' object has no attribute 'add'

    >>> 

    __new__() 方法接受的参数依次是:

    1>当前准备创建的类的对象

    2>类的名称

    3>类继承的父类集合

    4>类的方法集合

    IO 编程

    >>>f=open(‘/Users/BobZhou/Desktop/hello.py’) #r 表示读文件 这样就成功的打开了一个文件

    >>>f.read() #把文件读进内存中用一个 str 对象表示

    >>>f.close() 文件读取完必须关闭 因为 IO 操作并不是立刻进行的,只是先写进内存中,等操作系统空闲时再读写进磁盘,如果没有关闭操作,将出现文件丢失等问题

    try:

    f = open(‘   ’)

    print(f.read())

    finally:

    if f:

    f.close()

    这样写比较麻烦 ,可用 with … as 的写法来代替

    with open (‘’,’r’)as f:

    print(f.read())

    StringIO 和 BytesIO 

    很多时候数据读写并不一定是文件,也可以在内存中读写, StringIO 就是在内存中读写 str

    >>> from io import StringIO

    >>> f = StringIO()

    >>> f.write('hello')

    5

    >>> f.write(' ')

    1

    >>> f.write('world!')

    6

    >>> print(f.getvalue())

    hello world!

    getvalue() 用于获取写入后的 str

    StringIO 操作的只能是 str, 如果要操作二进制数据,就需要使用 BytesIO ,然后写入一些 bytes:

    >>> from io import BytesIO

    >>> f = BytesIO()

    >>> f.write('中文'.encode('utf-8'))

    6

    >>> print(f.getvalue())

    b'xe4xb8xadxe6x96x87'

    操作文件和目录

    os.path.abspath(‘.’)# 查看当前目录的绝对路径

    os.path.join(‘users/bobzhou/desktop’,’testdir’)#在某个目录下创建一个新目录,首先把新目录的完整路径表示出来

    os.mkdir(‘users/bobzhou/desktop/testdir’)#然后创建一个目录

    os.rmdir(‘/users/bobzhou/desktop/testdir’)#删除一个目录

    把两个路径合成一个时,不要直接拼接字符串,而是通过os.path.join()函数,这样可以正确处理不同操作系统的路径分隔符

    要拆分路劲时,也不要直接去拆字符串,通过 os.path.split()函数,后一部分总是最后级别的目录或文件名

    os.path.splitext()  可以直接获得文件扩展名,这些合并拆分路径的函数并不要求目录和文件真实存在,他们只对字符串进行操作. os.rename(‘test.text’,’test.py’)# 重命名

    os.remove(‘test.py’)# 删除文件

    os.listdir(path) 可以获得路径下的文件列表 返回的是一个 list

    序列化与反序列化

    >>> import pickle

    >>> d  = dict(name = 'bob',age = 20,score = 90)

    >>> pickle.dumps(d)

    b'x80x03}qx00(Xx04x00x00x00nameqx01Xx03x00x00x00bobqx02Xx03x00x00x00ageqx03Kx14Xx05x00x00x00scoreqx04KZu.'

    也可以直接序列化写入一个文件 ,然后通过 pickle.load() 函数反序列化出对象

    >> f= open('dump.text','wb')

    >>> pickle.dump(d,f)

    >>> f.close()

    >>> f= open('dump.text','rb')

    >>> d = pickle.load(f)

    >>> f.close()

    >>> d

    {'name': 'bob', 'age': 20, 'score': 90}

    >>> 变量的内容又回来了,但是这个变量和原来的变量是完全不相干的对象,他们只是内容相同而已

    JSON 的序列化与反序列化

    Python 内置的 json 模块提供了 Python 对象到 json 格式的转换

    import json

    d = dict(name = ‘bob’,age = 20,score = 90)

    data = json.dumps(d) # 把字典对象序列化成一个 json

    reborn = json.loads(data)#反序列化

    当然只操作字典对象没什么意义,有时候需要把一个类序列化为 json表示,如果直接写肯定会报错,原因就是不知道怎么把一个类对象转化为 json, 这里可以把 class 的实例变为 dict,因为通常 class 实例都有一个__ dict__的属性,他就是一个 dict,用来存储实例变量,也有少数例外,比如定义了__ slots__ 的 class, 同样的道理,如果我们要把 json 反序列化我一个对象,loads() 函数首先转化为一个 dict对象,然后我们传入 object_hook 函数负责把 dict转换为 student 实例

    >>> import json

    >>> class Student (object):

    def __init__(self,name,age,score):

    self.name = name

    self.age = age

    self.score = score

    def __str__(self):

    return 'Student object(%s %s %s)'%(self.name,self.age,self.score)

    >>> s = Student('bob',20,90)

    >>> std_data = json.dumps(s,default = lambda obj:obj.__dict__)

    >>> print ('dump Stundent:',std_data)

    dump Stundent: {"age": 20, "name": "bob", "score": 90}

    >>> rebuild = json.loads(std_data,objct_hook = lambda d :Student(d['name'],d['age'],d['score']))

    Traceback (most recent call last):

      File "<pyshell#79>", line 1, in <module>

        rebuild = json.loads(std_data,objct_hook = lambda d :Student(d['name'],d['age'],d['score']))

      File "/usr/local/Cellar/python3/3.5.2/Frameworks/Python.framework/Versions/3.5/lib/python3.5/json/__init__.py", line 332, in loads

        return cls(**kw).decode(s)

    TypeError: __init__() got an unexpected keyword argument 'objct_hook'

    >>> rebuild = json.loads(std_data,object_hook = lambda d:Student(d['name'],d['age'],d['score']))

    >>> print(rebuild)

    Student object(bob 20 90)

    多线程 多进程 协程

    多进程:

    Unix/Linux 操作系统提供了一个 fork() 调用,他非常特殊,普通的函数调用,调用一次,返回一次,但是 fork() 调用一次,返回两次,因为操作系统自动把当前进程(父进程)复制了一份(子进程),然后分别在父进程和子进程里返回,子进程永远返回0 ,父进程返回子进程的 ID, 这样做的理由是一个父进程可以 fork 出很多子进程,所以父进程要记下每个子进程的 ID,而子进程只需要调用 getppid ()就可以拿到父进程的 ID.

    import os

    pid  = os.fork()

    windows 没有 fork() 调用, Mac 是基于 BSD 内核,所以有 fork 调用, Python 是跨平台的提供了 multiprocessing模块 ,一个 process类代表一个进程对象

    from multiprocessing import Process,Queue

    import os,time,random

    def write(q):

        print ('Process to write:(%s)' % os.getpid())

        for value in ['A','B','C']:

            print('put %s to queue' % value)

            q.put(value)

            time.sleep(random.random())

    def read(q):

        print('Process to read: %s' % os.getpid())

        while True:

            value = q.get(True)

            print('Get %s from queue' % value)

    if __name__ == '__main__':

        q = Queue()

        pw = Process(target=write,args = (q,))

        pr = Process(target=read,args = (q,))

        pw.start()

        pr.start()

        pw.join()

        pr.terminate()

    多线程:

    一个进程至少有一个线程,线程是操作系统直接支持的执行单元,因此高级语言都内置多线程的支持, Python 也不例外, Python 是真正的 POSIX thread, 而不是模拟出来的线程. Python 提供了两个模块, _thread,threading,启动一个线程就是把一个函数传入并创建 thread 实例,然后调用 start() 开始执行:

    import time, threading

    # 新线程执行的代码:

    def loop():

        print('thread %s is running...' % threading.current_thread().name)

        n = 0

        while n < 5:

            n = n + 1

            print('thread %s >>> %s' % (threading.current_thread().name, n))

            time.sleep(1)

        print('thread %s ended.' % threading.current_thread().name)

    print('thread %s is running...' % threading.current_thread().name)

    t = threading.Thread(target=loop, name='LoopThread')

    t.start()

    t.join()

    print('thread %s ended.' % threading.current_thread().name)

    多线程同时访问一个变量肯定会出现数据混乱的问题,因此 Python 也有线程锁

    balance = 0

    lock  = threading.Lock()

    def run_thread(n):

    for i in range(10000):

    lock.acquire() # 先获取锁

    try:

    change_it(n)#放心改吧

    finally:

    lock.release()#改完了一定要释放锁,不然别的线程无法继续访问

    多核 CPU

    要想把 N 核 CPU 的核心全部跑满,就必须启东 N 个死循环线程

    import threading,multiprocessing

    def loop():

    x = 0

    while True:

    x = x^1

    for i in range (multiprocessing.cpu_count()):

    t  = threading.Thread(target = loop)

    t.start()

    在4 核 CPU 上可以监控到 CPU 占用率仅有102%,也就是仅仅用了一核

    因为 Python 的线程虽然是真正的线程,但解释器执行代码时,会有一个 GIL 锁,任何线程执行前,必须获取锁,然后每执行100条代码,解释器就会自动释放 GIL 锁,让别的线程有机会执行,所以多线程在 Python 中只能交替执行,即使100个线程跑在100核 CPU 上,也只能用到一个核,要想真正利用多核,除非重写一个不带 GIL 的 Python 解释器.

    ThreadLocal

    在多线程环境下,每个线程都有自己的数据,一个线程使用自己的局部变量比使用全局变量好,因为局部变量不会影响其他线程,而全局变量的修改必须加锁,但是局部变量也有问题就是在函数调用时,传递起来麻烦

    import threading

    local_school = threading.local()

    def process_student():

    std = local_school.student

    print(‘hello,%s (in %s)’ % (std,threading.current_thread().name))

    def process_thread(name):

    local_school.student = name

    process_student()

    t1 = threading.Thread(target=process_thread,args = (‘Alice’,),name = ‘Thread-A’)

    t2 = threading.Thread(target=process_thread,args=(‘Bob’,),name = ‘Thread-B’)

    t1.start()

    t2.start()

    t1.join()

    t2.join()

    collections

    namedtuple()

    这是一个函数,它用来创建一个自定义的tuple对象,并且规定了tuple的元素个数,并可以用属性而不是索引来引用tuple的某个元素

    from collections import named tuple

    point  = namedtuple(‘point’.[‘x’,’y’])

    p = point(1.2)

    p.x p.y

    hash lib(MD5 SHA1)

    >>> import hashlib

    >>> md5 = hashlib.md5()

    >>> md5.update('hello'.encode('utf-8'))

    >>> print(md5.hexdigest())

    5d41402abc4b2a76b9719d911017c592 # 32位16进制字符

    >>> sha1 = hashlib.sha1()

    >>> sha1.update('hello'.encode('utf-8'))

    >>> print(sha1.hexdigest())

    aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d

    网络编程

    TCP 编程 和 UDP 编程

    电子邮件 SMTP 发送邮件  POP3 收取邮件

    访问数据库 

    web 开发 HTML 协议 WSGI 接口 web框架 

    异步 IO 

    这里主要说下 WSGI 接口,其他的比如 HTTP 协议一本书都写不完

    一个 web 应用的本质其实就是:

    1 浏览器发送一个HTTP请求;

    2 服务器收到请求,生成一个HTML文档;

    3 服务器把HTML文档作为HTTP响应的Body发送给浏览器;

    4 浏览器收到HTTP响应,从HTTP Body取出HTML文档并显示;

    所以最简单的 web 应用就是先把 HTML 用文件保存好,用一个现成的 HTTP 服务器软件,接收用户请求,从文件中读取 HTML, 返回. Apache,Nginx,Lighttpd 等这些静态服务器就是干这件事情的.

    如果要动态的生成 HTML, 就需要把上面的步骤自己来实现, WSGI 接口就是做这个的,他只要求开发者实现一个函数,就可以响应 HTTP 请求,来个最简单的 web版本Hello world

     def application(environ,start_response):

    start_response(‘200 OK ’,[(‘Content-Type’,’text/html’)])

    return [b’<h1>Hello,world</h1>’]

    在 application() 中调用 start_response()函数就发送了 HTTP 响应的 header, 然后函数的返回值将作为 HTTP 的 body 发送到浏览器

    不过这个函数怎么调用呢?他必须由 WSGI 的服务器来调用, Python 内置了一个,这个模块是 wsgiref.

    再编写一个 server.py

    from wsgiref.simple_server import make_server

    from hello import application # 从自己编写的 hello.py 导入 application() 函数

    # 创建一个服务器, IP 地址为空,端口8000,处理函数 application

    http = make_server(‘’,8000,application)

    print(‘Severing HTTP on port 8000…’)

    http.serve_forever()  # 开始监听 HTTP 请求

    启动成功后,打开浏览器,输入 http://localhost:8000/ 就可以看到结果了

  • 相关阅读:
    《那些年啊,那些事——一个程序员的奋斗史》——29
    《那些年啊,那些事——一个程序员的奋斗史》——28
    《那些年啊,那些事——一个程序员的奋斗史》——28
    《那些年啊,那些事——一个程序员的奋斗史》——29
    《那些年啊,那些事——一个程序员的奋斗史》——30
    《那些年啊,那些事——一个程序员的奋斗史》——29
    《那些年啊,那些事——一个程序员的奋斗史》——28
    小议如何改变指针的指向
    X Window Troubleshooting
    终于把傅雷的《世界美术名作二十讲》看完了
  • 原文地址:https://www.cnblogs.com/ChrisZhou666/p/5888618.html
Copyright © 2020-2023  润新知