python 廖雪峰学习笔记

目录

面向对象高级编程

使用__slots__

使用@property

多重继承

定制类

str()

iter()

使用枚举类

使用元类

type()

metaclass

错误、调试和测试

错误处理

try…except…finally…

调用栈

记录错误

抛出错误:

调试

断言:

logging

单元测试

文档测试

IO编程

文件读写

读文件

StringIO和BytesIO

StringIO:

BytesIO:

操作文件和目录

环境变量

操作文件和目录

序列化

进程和线程

多进程

多线程

ThreaLocal

进程 vs 线程

分布式进程

正则表达式


面向对象高级编程

使用__slots__

动态给实例绑定属性:

class Student(object):
    pass
s=Student()
# 动态给实例绑定一个属性
s.name='wangteng'
print(s.name)   #wangteng

给实例绑定一个方法:

#定义函数作为实例方法:
def set_age(self,age):
    self.age=age
from types import  MethodType
#给实例绑定一个方法:
s.set_age=MethodType(set_age,s)
s.set_age(25)
print(s.age)   #25
s2=Student()
s2.set_age(25)   #AttributeError: 'Student' object has no attribute 'set_age'
print(s2.age)

注意:一个实例绑定的方法,对另一个实例是不起作用的。

为了给所有实例都绑定方法,可以给class绑定方法:

def set_chengji(self,chengji):
    self.chengji=chengji
Student.set_chengji=set_chengji

s.set_chengji(100)
print(s.chengji)   #100
s2.set_chengji(99)
print(s2.chengji)   #99

使用__slots__:

Python允许在定义class的时候,定义一个特殊的__slots__变量,来限制该class实例能添加的属性:

class xuesheng(object):
    __slots__ = ('name','age')
#创建实例
x1=xuesheng()
#绑定属性’name‘
x1.name='wt'
#绑定属性’age‘
x1.age='25'
#绑定属性’chengji‘
x1.chengji=99

'''
输出结果:
Traceback (most recent call last):
  File "E:/pythonproject/pythonProject/面向对象高级编程/01使用__slots__.py", line 40, in <module>
    x1.chengji=99
AttributeError: 'xuesheng' object has no attribute 'chengji'
'''

注意:__slots__定义的属性仅对当前类实例起作用,对继承的子类是不起作用的:

class xuesheng1(xuesheng):
    pass
xx1=xuesheng1()
xx1.chengji=9999
print(xx1.chengji) #9999

除非在子类中也定义__slots__,这样,子类实例允许定义的属性就是自身的__slots__加上父类的__slots__。

使用@property

class changfangxing:
    def __init__(self,length,width):
        self.length=length
        self.width=width
        self.mianji=self.width*self.length

cfx=changfangxing(5,2)
print(cfx.width)  #2
print(cfx.length)  #5
print(cfx.mianji)  #10
cfx.mianji=100
print(cfx.mianji)  #100

此时的面积暴露在外面,可以任意更改。例如面积更改为100.

可以采用get方法但是太麻烦,因为是一个方法,不能当成一个属性进行调用:

class changfangxing:
    def __init__(self,length,width):
        self.length=length
        self.width=width

    def get_mianji(self):
        return self.width*self.length

cfx=changfangxing(5,2)
print(cfx.width)  #2
print(cfx.length)  #5
print(cfx.get_mianji())  #10

一个类里定义的方法一旦被@property修饰,可以像使用属性一样去使用这个方法:

class changfangxing:
    def __init__(self,length,width):
        self.length=length
        self.width=width
    @property
    def mianji(self):
        return self.width*self.length
cfx=changfangxing(5,2)
print(cfx.width)   #2
print(cfx.length)   #5
print(cfx.mianji)   #10

mianj虽然是一个方法,但是可以当成属性来使用,调用的时候后面不用加(),也不用担心对其赋值,因为赋值会报错:

class changfangxing:
    def __init__(self,length,width):
        self.length=length
        self.width=width
    @property
    def mianji(self):
        return self.width*self.length
cfx=changfangxing(5,2)
cfx.mianji=100
报错:
Traceback (most recent call last):
  File "E:/pythonproject/pythonProject/面向对象高级编程/02 使用@property.py", line 40, in <module>
    cfx.mianji=100
AttributeError: can't set attribute

参考视频:python干货分享---property的用法

多重继承

继承是面向对象编程的一个重要的方式,因为通过继承,子类就可以扩展父类的功能。

实例:C继承了A,B:

class A(object):
    pass
class B(object):
    pass
class C(A,B):
    pass

多重继承这个名词一般用来形容继承链条可以很长,多个层次。多继承则指一个类可以有多个基类,相反则是单继承。任何面向对象编程语言都支持多重继承,但像java这种只能通过接口实现有限程度的多继承。

mixin设计模式:

在设计类的继承关系时,通常,主线都是单一继承下来的,例如,苹果 继承自 水果。但是,如果需要“混入”额外的功能(例如是南方水果还是北方水果),通过多重继承就可以实现,比如,让苹果 除了继承自水果外,再同时继承北方水果。这种设计通常称之为MixIn。

MixIn的目的就是给一个类增加多个功能,这样,在设计类的时候,我们优先考虑通过多重继承来组合多个MixIn的功能,而不是设计多层次的复杂的继承关系。

多继承就是mixin模式。

看不懂可以看一下视频:mixin设计模式的应用(多继承应用场景)

定制类

str()

str()返回用户看到的字符串

class Student(object):
       def __init__(self, name):
          self.name = name
print(Student('Michael'))

输出结果:
C:\ProgramData\Anaconda3\python.exe "E:/pythonproject/pythonProject/面向对象高级编程/04 定制类.py"
<__main__.Student object at 0x000002E902E48400>
class Student(object):
    def __init__(self, name):
        self.name = name
    def __str__(self):
        return 'Student object (name=%s)' % self.name

print(Student('Michael'))  #Student object (name=Michael)

iter()

如果一个类想被用于for … in循环,类似list或tuple那样,就必须实现一个__iter__()方法,该方法返回一个迭代对象,然后,Python的for循环就会不断调用该迭代对象的__next__()方法拿到循环的下一个值,直到遇到StopIteration错误时退出循环。

以斐波那契数列为例:

class Fib(object):
    def __init__(self):
        self.a, self.b = 0, 1 # 初始化两个计数器a,b

    def __iter__(self):
        return self # 实例本身就是迭代对象,故返回自己

    def __next__(self):
        self.a, self.b = self.b, self.a + self.b # 计算下一个值
        if self.a > 100000: # 退出循环的条件
            raise StopIteration()
        return self.a # 返回下一个值
for n in Fib():
    print(n)

使用枚举类

为月份这样的枚举类型定义一个class类型,每个常量都是class的一个唯一实例。Python提供了Enum类来实现这个功能:

from enum import Enum
Month = Enum('Month', ('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'))
for name, member in Month.__members__.items():
    print(name, '=>', member, ',', member.value)
'''
输出结果:
Jan => Month.Jan , 1
Feb => Month.Feb , 2
Mar => Month.Mar , 3
Apr => Month.Apr , 4
May => Month.May , 5
Jun => Month.Jun , 6
Jul => Month.Jul , 7
Aug => Month.Aug , 8
Sep => Month.Sep , 9
Oct => Month.Oct , 10
Nov => Month.Nov , 11
Dec => Month.Dec , 12
'''
print(Month.Feb.value)  #2

value属性则是自动赋给成员的int常量,默认从1开始计数。

如果需要更精确地控制枚举类型,可以从Enum派生出自定义类:

#从Enum派生出自定义类:
from enum import Enum, unique
@unique
class Weekday(Enum):
    Sun = 0 # Sun的value被设定为0
    Mon = 1
    Tue = 2
    Wed = 3
    Thu = 4
    Fri = 5
    Sat = 6
print(Weekday.Sun)  #Weekday.Sun
print(Weekday.Sun.value)  #0
print(Weekday(2))   #Weekday.Tue

@unique装饰器可以帮助我们检查保证没有重复值。

由此可见,既可以用成员名称引用枚举常量,又可以直接根据value的值获得枚举常量。

使用元类

type()

type()函数可以查看一个类型或变量的类型:

class Hello(object):
    def hello(self, name='world'):
        print('Hello, %s.' % name)
from 面向对象高级编程.Hello import Hello
h=Hello()
print(h.hello('wt'))
'''
输出结果:
Hello, wt.
None
'''
print(type(Hello()))  #<class '面向对象高级编程.Hello.Hello'>
print(type(Hello))  #<class 'type'>
print(type(h))   #<class '面向对象高级编程.Hello.Hello'>

Hello是一个class,它的类型是type,h是一个实例,它的类型就是class hello。

type()函数既可以返回一个对象的类型,又可以创建出新的类型

type()函数创建出Hello类:

def fn(self,name='world'):   # 先定义函数
    print('hello,%s'%name)
Hello = type('Hello', (object,), dict(hello=fn)) # 创建Hello class
h = Hello()
print(h.hello())
'''
输出结果:
hello,world
None
'''

要创建一个class对象,type()函数依次传入3个参数:

  1. class的名称;
  2. 继承的父类集合,注意Python支持多重继承,如果只有一个父类,别忘了tuple的单元素写法;
  3. class的方法名称与函数绑定,这里我们把函数fn绑定到方法名hello。

metaclass

metaclass,直译为元类,一般不会用到。

先定义metaclass,就可以创建类,最后创建实例。

metaclass允许你创建类或者修改类。换句话说,你可以把类看成是metaclass创建出来的“实例”。

错误、调试和测试

错误处理

try…except…finally…

try:
    print('try...')
    r = 10 / 0
    print('result:', r)
except ZeroDivisionError as e:
    print('except:', e)
finally:
    print('finally...')
print('END')
'''
执行结果:
try...
except: division by zero
finally...
END
'''

先执行try的代码段,若try的的代码段里执行出错,后续代码不会执行,直接跳转至错误处理代码,except语句块,执行完except后,如果有finally语句块,则执行finally语句块,至此,执行完毕。

若try的的代码段里没有错误发生,except语句块不会被执行,但是finally如果有,则一定会被执行(可以没有finally语句)。

Python所有的错误都是从BaseException类派生的,常见的错误类型和继承关系看这里:

https://docs.python.org/3/library/exceptions.html#exception-hierarchy

调用栈

如果错误没有被捕获,它就会一直往上抛,最后被Python解释器捕获,打印一个错误信息,然后程序退出。

def foo(s):
    return 10 / int(s)

def bar(s):
    return foo(s) * 2

def main():
    bar('0')

main()
'''
执行结果:
Traceback (most recent call last):    #错误的跟踪信息
  File "E:/pythonproject/pythonProject/python/07 错误、调试和测试/01 错误处理.py", line 27, in <module>
    main()  #调用main()出错了,原因在第27行
  File "E:/pythonproject/pythonProject/python/07 错误、调试和测试/01 错误处理.py", line 25, in main
    bar('0')  #调用 bar('0')出错了,原因在25行
  File "E:/pythonproject/pythonProject/python/07 错误、调试和测试/01 错误处理.py", line 22, in bar
    return foo(s) * 2  #return foo(s) * 2 语句出错了,原因在22行
  File "E:/pythonproject/pythonProject/python/07 错误、调试和测试/01 错误处理.py", line 19, in foo
    return 10 / int(s)   # return 10 / int(s)出错了,这是错误产生的源头,因为下面打印了:
ZeroDivisionError: division by zero
'''

出错的时候,一定要分析错误的调用栈信息,才能定位错误的位置。

记录错误

Python内置的logging模块可以非常容易地记录错误信息,同时程序会继续执行下去:

import logging

def foo(s):
    return 10 / int(s)

def bar(s):
    return foo(s) * 2

def main():
    try:
        bar('0')
    except Exception as e:
        logging.exception(e)

main()
print('END')
'''
输出结果:
ERROR:root:division by zero
Traceback (most recent call last):
  File "E:/pythonproject/pythonProject/python/07 错误、调试和测试/01 错误处理.py", line 53, in main
    bar('0')
  File "E:/pythonproject/pythonProject/python/07 错误、调试和测试/01 错误处理.py", line 49, in bar
    return foo(s) * 2
  File "E:/pythonproject/pythonProject/python/07 错误、调试和测试/01 错误处理.py", line 46, in foo
    return 10 / int(s)
ZeroDivisionError: division by zero
END
'''

抛出错误:

如果要抛出错误,首先根据需要,可以定义一个错误的class,选择好继承关系,然后,用raise语句抛出一个错误的实例:

class FooError(ValueError):
    pass

def foo(s):
    n = int(s)
    if n==0:
        raise FooError('invalid value: %s' % s)
    return 10 / n

foo('0')
'''
执行结果:
Traceback (most recent call last):
  File "E:/pythonproject/pythonProject/python/07 错误、调试和测试/01 错误处理.py", line 81, in <module>
    foo('0')
  File "E:/pythonproject/pythonProject/python/07 错误、调试和测试/01 错误处理.py", line 78, in foo
    raise FooError('invalid value: %s' % s)
__main__.FooError: invalid value: 0
'''

调试

用print(),把有可能的变量打印出来,坏处是要删除很多print(),运行结果包含很多垃圾信息。

断言:

凡是用print()来辅助查看的地方,都可以用断言(assert)来替代:

def foo(s):
    n = int(s)
    assert n != 0, 'n is zero!'
    return 10 / n

foo('0')
'''
运行结果:
Traceback (most recent call last):
  File "E:/pythonproject/pythonProject/python/07 错误、调试和测试/02 调试.py", line 6, in <module>
    foo('0')
  File "E:/pythonproject/pythonProject/python/07 错误、调试和测试/02 调试.py", line 3, in foo
    assert n != 0, 'n is zero!'
AssertionError: n is zero!
'''

如果断言失败,assert语句本身就会抛出AssertionError。

程序中如果到处充斥着assert,和print()相比也好不到哪去。不过,启动Python解释器时可以用-O参数来关闭assert:

$ python -O err.py
Traceback (most recent call last):
  ...
ZeroDivisionError: division by zero

注意:断言的开关“-O”是英文大写字母O,不是数字0。

logging

logging不会抛出错误,而且可以输出到文件:

import logging
logging.basicConfig(level=logging.INFO)
s = '0'
n = int(s)
logging.info('n = %d' % n)
print(10 / n)

'''
输出结果:
INFO:root:n = 0
Traceback (most recent call last):
  File "E:/pythonproject/pythonProject/python/07 错误、调试和测试/02 调试.py", line 24, in <module>
    print(10 / n)
ZeroDivisionError: division by zero
'''

logging的好处,它允许你指定记录信息的级别,有debug,info,warning,error等几个级别,当我们指定level=info时,logging.debug就不起作用了。同理,指定level=warning后,debug和info就不起作用了。这样一来,你可以放心地输出不同级别的信息,也不用删除,最后统一控制输出哪个级别的信息。

logging的另一个好处是通过简单的配置,一条语句可以同时输出到不同的地方,比如console和文件。

虽然用IDE调试起来比较方便,但是最后你会发现,logging才是终极武器。

单元测试

单元测试是用来对一个模块、一个函数或者一个类来进行正确性检验的测试工作。

小结:

  • 单元测试可以有效地测试某个程序模块的行为,是未来重构代码的信心保证。
  • 单元测试的测试用例要覆盖常用的输入组合、边界条件和异常。
  • 单元测试代码要非常简单,如果测试代码太复杂,那么测试代码本身就可能有bug。
  • 单元测试通过了并不意味着程序就没有bug了,但是不通过程序肯定有bug。

文档测试

文档测试(doctest)模块可以直接提取注释中的代码并执行测试,只有在命令行下才能直接运行。

用测试文档来测试编写的Dict类:

class Dict(dict):
    '''
    Simple dict but also support access as x.y style.

    >>> d1 = Dict()
    >>> d1['x'] = 100
    >>> d1.x
    100
    >>> d1.y = 200
    >>> d1['y']
    200
    >>> d2 = Dict(a=1, b=2, c='3')
    >>> d2.c
    '3'
    >>> d2['empty']
    Traceback (most recent call last):
        ...
    KeyError: 'empty'
    >>> d2.empty
    Traceback (most recent call last):
        ...
    AttributeError: 'Dict' object has no attribute 'empty'
    '''
    def __init__(self, **kw):
        super(Dict, self).__init__(**kw)

    def __getattr__(self, key):
        try:
            return self[key]
        except KeyError:
            raise AttributeError(r"'Dict' object has no attribute '%s'" % key)

    def __setattr__(self, key, value):
        self[key] = value

if __name__=='__main__':
    import doctest
    doctest.testmod()

没有输出,说明编写的是正确的,如果程序有问腿,会报错,例如:把__getatter()__方法注释,会报以下错误:

<re.Match object; span=(3, 6), match='def'>
def
**********************************************************************
File "E:/pythonproject/pythonProject/python/07 错误、调试和测试/04 文档测试.py", line 13, in __main__.Dict
Failed example:
    d1.x
Exception raised:
    Traceback (most recent call last):
      File "C:\ProgramData\Anaconda3\lib\doctest.py", line 1336, in __run
        exec(compile(example.source, filename, "single",
      File "<doctest __main__.Dict[2]>", line 1, in <module>
        d1.x
    AttributeError: 'Dict' object has no attribute 'x'
**********************************************************************
File "E:/pythonproject/pythonProject/python/07 错误、调试和测试/04 文档测试.py", line 19, in __main__.Dict
Failed example:
    d2.c
Exception raised:
    Traceback (most recent call last):
      File "C:\ProgramData\Anaconda3\lib\doctest.py", line 1336, in __run
        exec(compile(example.source, filename, "single",
      File "<doctest __main__.Dict[6]>", line 1, in <module>
        d2.c
    AttributeError: 'Dict' object has no attribute 'c'
**********************************************************************
1 items had failures:
   2 of   9 in __main__.Dict
***Test Failed*** 2 failures.

当模块正常导入时,doctest不会被执行。只有在命令行直接运行时,才执行doctest。所以,不必担心doctest会在非测试环境下执行。

IO编程

文件读写

       读写文件就是请求操作系统打开一个文件对象(通常称为文件描述符),然后,通过操作系统提供的接口从这个文件对象中读取数据(读文件),或者把数据写入这个文件对象(写文件)。

读文件

open() 函数用于创建或打开指定文件,该函数的常用语法格式如下:

file = open(file_name [, mode='r' [ , buffering=-1 [ , encoding = None ]]])

此格式中,用 [] 括起来的部分为可选参数,即可以使用也可以省略。其中,各个参数所代表的含义如下:

  • file:表示要创建的文件对象。
  • file_name:要创建或打开文件的文件名称,该名称要用引号(单引号或双引号都可以)括起来。需要注意的是,如果要打开的文件和当前执行的代码文件位于同一目录,则直接写文件名即可;否则,此参数需要指定打开文件所在的完整路径。
  • mode:可选参数,用于指定文件的打开模式。可选的打开模式如表 1 所示。如果不写,则默认以只读(r)模式打开文件。
  • buffering:可选参数,用于指定对文件做读写操作时,是否使用缓冲区(本节后续会详细介绍)。
  • encoding:手动设定打开文件时所使用的编码格式,不同平台的 ecoding 参数值也不同,以 Windows 为例,其默认为 cp936(实际上就是 GBK 编码)。

open() 函数支持的文件打开模式如表 1 所示。

表 1 open 函数支持的文件打开模式

模式意义注意事项
r只读模式打开文件,读文件内容的指针会放在文件的开头。操作的文件必须存在。
rb以二进制格式、采用只读模式打开文件,读文件内容的指针位于文件的开头,一般用于非文本文件,如图片文件、音频文件等。
r+打开文件后,既可以从头读取文件内容,也可以从开头向文件中写入新的内容,写入的新内容会覆盖文件中等长度的原有内容。
rb+以二进制格式、采用读写模式打开文件,读写文件的指针会放在文件的开头,通常针对非文本文件(如音频文件)。
w以只写模式打开文件,若该文件存在,打开时会清空文件中原有的内容。若文件存在,会清空其原有内容(覆盖文件);反之,则创建新文件。
wb以二进制格式、只写模式打开文件,一般用于非文本文件(如音频文件)
w+打开文件后,会对原有内容进行清空,并对该文件有读写权限。
wb+以二进制格式、读写模式打开文件,一般用于非文本文件
a以追加模式打开一个文件,对文件只有写入权限,如果文件已经存在,文件指针将放在文件的末尾(即新写入内容会位于已有内容之后);反之,则会创建新文件。
ab以二进制格式打开文件,并采用追加模式,对文件只有写权限。如果该文件已存在,文件指针位于文件末尾(新写入文件会位于已有内容之后);反之,则创建新文件。
a+以读写模式打开文件;如果文件存在,文件指针放在文件的末尾(新写入文件会位于已有内容之后);反之,则创建新文件。
ab+以二进制模式打开文件,并采用追加模式,对文件具有读写权限,如果文件存在,则文件指针位于文件的末尾(新写入文件会位于已有内容之后);反之,则创建新文件。

调用close()方法关闭文件。文件使用完毕后必须关闭,因为文件对象会占用操作系统的资源,并且操作系统同一时间能打开的文件数量也是有限的。

Python引入了with语句来自动帮我们调用close()方法:

with open('/path/to/file', 'r') as f:
    print(f.read())

按行读写:

for line in f.readlines():
    print(line.strip()) # 把末尾的'\n'删掉

二进制文件:

要读取二进制文件,比如图片、视频等等,用'rb'模式打开文件即可:

f = open('/Users/michael/test.jpg', 'rb')
f.read()
'''
输出结果:
b'\xff\xd8\xff\xe1\x00\x18Exif\x00\x00...' # 十六进制表示的字节
'''

遇到有些编码不规范的文件,你可能会遇到UnicodeDecodeError,因为在文本文件中可能夹杂了一些非法编码的字符。遇到这种情况,open()函数还接收一个errors参数,表示如果遇到编码错误后如何处理。最简单的方式是直接忽略:

 f = open('/Users/michael/gbk.txt', 'r', encoding='gbk', errors='ignore')

写文件:

写文件和读文件是一样的,唯一区别是调用open()函数时,传入标识符'w'或者'wb'表示写文本文件或写二进制文件:

f = open('/Users/michael/test.txt', 'w')
f.write('Hello, world!')
f.close()

#用with:
with open('/Users/michael/test.txt', 'w') as f:
    f.write('Hello, world!')

'w'模式写入文件时,如果文件已存在,会直接覆盖(相当于删掉后新写入一个文件)。如果我们希望追加到文件末尾怎么办?可以传入'a'以追加(append)模式写入。

StringIO和BytesIO

StringIO:

StringIO顾名思义就是在内存中读写str。

要把str写入StringIO,我们需要先创建一个StringIO,然后,像文件一样写入即可:

from io import StringIO
f=StringIO()
f.write('hello')
f.write(' ')
f.write('world')
print(f.getvalue())

#输出结果:
hello world

#方法二:像读文件一样读取:
from  io import StringIO
f=StringIO('hello!\nhi!\nwangteng!')
while True:
    s=f.readline()
    if s=='':
        break
    print(s.strip())
#输出结果:
hello!
hi!
wangteng!

getvalue()方法用于获得写入后的str。

BytesIO:

StringIO操作的只能是str,如果要操作二进制数据,就需要使用BytesIO。

from io import  BytesIO
f=BytesIO()
f.write('中文'.encode('utf-8'))
print(f.getvalue())
#输出结果:b'\xe4\xb8\xad\xe6\x96\x87'

#方法二:像读取文件一样读取:
from io import  BytesIO
f=BytesIO(b'\xe4\xb8\xad\xe6\x96\x87')
f.read()
#输出结果:b'\xe4\xb8\xad\xe6\x96\x87'

注意:写入的不是str,而是经过UTF-8编码的bytes。

操作文件和目录

import os
print(os.name)  #操作系统类型
#输出结果:nt 代表是windows系统

环境变量

在操作系统中定义的环境变量,全部保存在os.environ这个变量中,可以直接查看:

print(os.environ)
#输出结果:
#environ({'ALLUSERSPROFILE': 'C:\\ProgramData', 'APPDATA': 'C:\\Users\\13108\\AppData\\Roaming', 'COMMONPROGRAMFILES': 'C:\\Program Files\\Common Files', 'COMMONPROGRAMFILES(X86)': 'C:\\Program Files (x86)\\Common Files', 'COMMONPROGRAMW6432': 'C:\\Program Files\\Common Files', 'COMPUTERNAME': '豚豚', 'COMSPEC': 'C:\\Windows\\system32\\cmd.exe', 'CONDA_DEFAULT_ENV': 'base', 'CONDA_PREFIX': 'C:\\ProgramData\\Anaconda3', 'CONDA_PROMPT_MODIFIER': '(base) ', 'CONDA_SHLVL': '1', 'DRIVERDATA': 'C:\\Windows\\System32\\Drivers\\DriverData', 'FPS_BROWSER_APP_PROFILE_STRING': 'Internet Explorer', 'FPS_BROWSER_USER_PROFILE_STRING': 'Default', 'HOMEDRIVE': 'C:', 'HOMEPATH': '\\Users\\13108', 'IDEA_INITIAL_DIRECTORY': 'C:\\Users\\13108\\Desktop', 'LOCALAPPDATA': 'C:\\Users\\13108\\AppData\\Local', 'LOGONSERVER': '\\\\豚豚', 'NUMBER_OF_PROCESSORS': '12', 'ONEDRIVE': 'C:\\Users\\13108\\OneDrive', 'OS': 'Windows_NT', 'PATH': 'C:\\ProgramData\\Anaconda3;C:\\ProgramData\\Anaconda3\\Library\\mingw-w64\\bin;C:\\ProgramData\\Anaconda3\\Library\\usr\\bin;C:\\ProgramData\\Anaconda3\\Library\\bin;C:\\ProgramData\\Anaconda3\\Scripts;C:\\ProgramData\\Anaconda3\\bin;C:\\ProgramData\\Anaconda3\\condabin;C:\\ProgramData\\Anaconda3;C:\\ProgramData\\Anaconda3\\Library\\mingw-w64\\bin;C:\\ProgramData\\Anaconda3\\Library\\usr\\bin;C:\\ProgramData\\Anaconda3\\Library\\bin;C:\\ProgramData\\Anaconda3\\Scripts;C:\\Windows\\system32;C:\\Windows;C:\\Windows\\System32\\Wbem;C:\\Windows\\System32\\WindowsPowerShell\\v1.0;C:\\Windows\\System32\\OpenSSH;C:\\Program Files (x86)\\NVIDIA Corporation\\PhysX\\Common;C:\\Program Files\\NVIDIA Corporation\\NVIDIA NvDLISR;D:\\杞\ue219欢\\Bandizip;C:\\Users\\13108\\AppData\\Local\\Microsoft\\WindowsApps;.;C:\\Users\\13108\\AppData\\Local\\Programs\\Microsoft VS Code\\bin', 'PATHEXT': '.COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC', 'PROCESSOR_ARCHITECTURE': 'AMD64', 'PROCESSOR_IDENTIFIER': 'AMD64 Family 25 Model 80 Stepping 0, AuthenticAMD', 'PROCESSOR_LEVEL': '25', 'PROCESSOR_REVISION': '5000', 'PROGRAMDATA': 'C:\\ProgramData', 'PROGRAMFILES': 'C:\\Program Files', 'PROGRAMFILES(X86)': 'C:\\Program Files (x86)', 'PROGRAMW6432': 'C:\\Program Files', 'PROMPT': '(base) $P$G', 'PSMODULEPATH': 'C:\\Program Files\\WindowsPowerShell\\Modules;C:\\Windows\\system32\\WindowsPowerShell\\v1.0\\Modules', 'PUBLIC': 'C:\\Users\\Public', 'PYCHARM_HOSTED': '1', 'PYTHONIOENCODING': 'UTF-8', 'PYTHONPATH': 'E:\\pythonproject\\pythonProject', 'PYTHONUNBUFFERED': '1', 'SESSIONNAME': 'Console', 'SYSTEMDRIVE': 'C:', 'SYSTEMROOT': 'C:\\Windows', 'TEMP': 'C:\\Users\\13108\\AppData\\Local\\Temp', 'TMP': 'C:\\Users\\13108\\AppData\\Local\\Temp', 'USERDOMAIN': '豚豚', 'USERDOMAIN_ROAMINGPROFILE': '豚豚', 'USERNAME': '13108', 'USERPROFILE': 'C:\\Users\\13108', 'WINDIR': 'C:\\Windows'})

获取某个环境变量的值:

print(os.environ.get('PATH'))
#输出结果:
#C:\ProgramData\Anaconda3;C:\ProgramData\Anaconda3\Library\mingw-w64\bin;C:\ProgramData\Anaconda3\Library\usr\bin;C:\ProgramData\Anaconda3\Library\bin;C:\ProgramData\Anaconda3\Scripts;C:\ProgramData\Anaconda3\bin;C:\ProgramData\Anaconda3\condabin;C:\ProgramData\Anaconda3;C:\ProgramData\Anaconda3\Library\mingw-w64\bin;C:\ProgramData\Anaconda3\Library\usr\bin;C:\ProgramData\Anaconda3\Library\bin;C:\ProgramData\Anaconda3\Scripts;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0;C:\Windows\System32\OpenSSH;C:\Program Files (x86)\NVIDIA Corporation\PhysX\Common;C:\Program Files\NVIDIA Corporation\NVIDIA NvDLISR;D:\杞欢\Bandizip;C:\Users\13108\AppData\Local\Microsoft\WindowsApps;.;C:\Users\13108\AppData\Local\Programs\Microsoft VS Code\bin
print(os.environ.get('x','default'))
# 输出结果:default

操作文件和目录

#查看当前目录的绝对路径:
print(os.path.abspath('.'))  #E:\pythonproject\pythonProject\python\08 IO编程

# 在某个目录下创建一个新目录,首先把新目录的完整路径表示出来:
print(os.path.join('E:/pythonproject/pythonProject/python/08 IO编程','testdir'))
#输出结果:E:/pythonproject/pythonProject/python/08 IO编程\testdir

# 然后创建一个目录:
os.mkdir('E:/pythonproject/pythonProject/python/08 IO编程/testdir')

#删除一个目录:
os.rmdir('E:/pythonproject/pythonProject/python/08 IO编程/testdir')

#拆分路径:
print(os.path.split('E:/pythonproject/pythonProject/python/08 IO编程/testdir/file.txt'))
#输出结果:('E:/pythonproject/pythonProject/python/08 IO编程/testdir', 'file.txt')

#得到文件扩展名:
print(os.path.splitext('E:/pythonproject/pythonProject/python/08 IO编程/testdir/file.txt'))
#输出结果:('E:/pythonproject/pythonProject/python/08 IO编程/testdir/file', '.txt')

#对文件重命名:
os.renames('test','test.py')
#删除文件:
os.remove('test.py')

#列出当前目录下的所有目录:
print([x for x in os.listdir('.') if os.path.isdir(x)])
#输出结果:['testdir']
#列出所有的.py文件:
print([x for x in os.listdir('.') if os.path.isfile(x)and os.path.splitext(x)[1]=='.py'])
#输出结果:['01 文件读写.py', '02 StringIo.py', '03 操作文件和目录.py', '__init__.py']

序列化

把变量从内存中变成可存储或传输的过程称之为序列化,在Python中叫pickling。

#把一个对象序列化写入文件:
import  pickle
d= dict(name='wangteng',age=26 ,score=98)
# pickle.dumps() 方法是把任意对象序列化成一个bytes,然后把bytes写入文件
print(pickle.dumps(d))
#输出结果:b'\x80\x04\x95)\x00\x00\x00\x00\x00\x00\x00}\x94(\x8c\x04name\x94\x8c\x08wangteng\x94\x8c\x03age\x94K\x1a\x8c\x05score\x94Kbu.'

#另一种方法:pickle.dump()直接把对象序列化后写入一个file-like  Object:
f=open('dump.text','wb')
pickle.dump(d,f)
f.close()

f=open('dump.text','rb')
d=pickle.load(f)  #反序列化出对象
f.close()
print(d)
#输出结果:{'name': 'wangteng', 'age': 26, 'score': 98}

#将python对象编程JSON:
import json
d=dict(name='wangteng',age=20,chengji=89)
print(json.dumps(d))
#输出结果:{"name": "wangteng", "age": 20, "chengji": 89}

#将JSON反序列化为python对象:
json_str='{"name":"wangteng","age":20,"chengji":89}'
print(json.loads(json_str))
#输出结果:{'name': 'wangteng', 'age': 20, 'chengji': 89}

#json的进阶:
import json
class student(object):
    def __init__(self,name,age,chengji):
        self.name=name
        self.age=age
        self.chengji=chengji

s=student('wangteng',20,88)
#print(json.dumps(s))

def student2dict(std):
    return {
        'name':std.name,
        'age':std.age,
        'chengji':std.chengji
    }
print(json.dumps(s,default=student2dict))
#输出结果:{"name": "wangteng", "age": 20, "chengji": 88}

进程和线程

进程:是计算机资源分配的最小单位(进程为线程提供资源)。

线程:是计算机中可以被cpu调度的最小单位(真正在工作)。

一个进程中可以有多个线程,同一个进程中的线程可以共享此进程中的资源。

  • 计算密集型:用多进程,例如:大量的数据计算。
  • IO密集型:用多线程,例如:文件读写,网络数据传输。

多进程

多线程

多线程和多进程的时间比较:

import time
import requests

url_list=[
    ("线程:死锁.mp4","https://www.bilibili.com/video/BV1Ev411G7i3?p=7&vd_source=43a8b5ecb97eee8636fd8ac7ae8151ca"),
    ("线程:总结和作业.mp4","https://www.bilibili.com/video/BV1Ev411G7i3?p=10&vd_source=43a8b5ecb97eee8636fd8ac7ae8151ca"),
    ("进程:今日概要.mp4","https://www.bilibili.com/video/BV1Ev411G7i3?p=12&vd_source=43a8b5ecb97eee8636fd8ac7ae8151ca")
]
print(time.time())
for file_name,url in url_list:
    res = requests.get(url)
    with open(file_name,mode='wb') as f:
        f.write(res.content)
    print(file_name,time.time())
'''
1655949667.968719
线程:死锁.mp4 1655949668.308015
线程:总结和作业.mp4 1655949668.9305954
进程:今日概要.mp4 1655949669.2900176
'''

#使用多线程:
import threading,time,requests
url_list=[
    ("线程:死锁.mp4","https://www.bilibili.com/video/BV1Ev411G7i3?p=7&vd_source=43a8b5ecb97eee8636fd8ac7ae8151ca"),
    ("线程:总结和作业.mp4","https://www.bilibili.com/video/BV1Ev411G7i3?p=10&vd_source=43a8b5ecb97eee8636fd8ac7ae8151ca"),
    ("进程:今日概要.mp4","https://www.bilibili.com/video/BV1Ev411G7i3?p=12&vd_source=43a8b5ecb97eee8636fd8ac7ae8151ca")
]
def task(file_name,url):
    res=requests.get(url)
    with open(file_name,'wb') as f:
        f.write(res.content)
    print(time.time())

print(time.time())
for name,url in url_list:
    # 创建线程,让每个线程都去执行task函数(参数不同)
    t=threading.Thread(target=task,args=(name,url))
    t.start()

'''
输出结果:
1655971437.7154496
1655971438.0637615
1655971438.0908408
1655971438.095749
'''

#使用多进程:
import time,requests,multiprocessing
url_list=[
    ("线程:死锁.mp4","https://www.bilibili.com/video/BV1Ev411G7i3?p=7&vd_source=43a8b5ecb97eee8636fd8ac7ae8151ca"),
    ("线程:总结和作业.mp4","https://www.bilibili.com/video/BV1Ev411G7i3?p=10&vd_source=43a8b5ecb97eee8636fd8ac7ae8151ca"),
    ("进程:今日概要.mp4","https://www.bilibili.com/video/BV1Ev411G7i3?p=12&vd_source=43a8b5ecb97eee8636fd8ac7ae8151ca")
]
def task(file_name,url):
    res=requests.get(url)
    with open(file_name,'wb') as f:
        f.write(res.content)
    print(time.time())

if __name__ == '__main__':
    print(time.time())
    for name,url in url_list:
        # 创建进程
        #Linux系统fork;win:spawn;mac支持:fork,spawn(python3.8默认设置spawn)
        t=multiprocessing.Process(target=task,args=(name,url))
        t.start()

'''
输出结果:
1655971844.1602507
1655971844.7430584
1655971844.7451484
1655971844.776326
'''


线程常见的方法:

  • t.start(),当前进程准备就绪(等待cpu调度,具体时间由cpu决定)。
import threading

loop=10000
number=0

def _add(count):
    global number
    for i in range(count):
        number+=1

t = threading.Thread(target=_add,args=(loop,))
t.start()

print(number)
'''
输出结果:
10000
'''
  • t.join(),等待当前进程的任务执行完毕后再向下继续执行。
import threading

number=0

def _add():
    global number
    for i in range(1000000):
        number+=1

def _sub():
    global number
    for i in range(1000000):
        number-=1


t1=threading.Thread(target=_add)
t2=threading.Thread(target=_sub)
t1.start()
t1.join()  #t1线程执行完毕后,才继续往后走
t2.start()
t2.join() #t2线程执行完毕后,才继续往后走

print(number)
'''
输出结果:0
'''

import  threading
loop =10000000
number=0

def _add(count):
    global number
    for i in range(count):
        number+=1

def _sub(count):
    global number
    for i in range(count):
        number-=1

t1=threading.Thread(target=_add,args=(loop,))
t2=threading.Thread(target=_sub,args=(loop,))
t1.start()
t2.start()
t1.join()  #t1线程执行完毕后,才继续往后走
t2.join() #t2线程执行完毕后,才继续往后走
print(number)
'''
输出结果:每次都不是确定的。原因是t1.start和t2.start的执行是按时间片轮换来执行的,可能会出现t1调用_add时此时计算到应该+1时,时间片用完了,执行t2的_sub时计算完—1时间片用完,此时的number和t
1调用_add的上次时间片末的number不一样,就会出现问题。
'''


出现上述问题的解决方法:加锁(只有一把锁)

import  threading

lock_obj=threading.RLock()

loop =10000000
number=0

def _add(count):
    lock_obj.acquire()  #加锁
    global number
    for i in range(count):
        number+=1
    lock_obj.release()   #释放锁

def _sub(count):
    lock_obj.acquire()   #申请锁(若锁没有被占用则加锁,若被占用则等待释放后再占用)
    global number
    for i in range(count):
        number-=1
    lock_obj.release()   #释放锁

t1=threading.Thread(target=_add,args=(loop,))
t2=threading.Thread(target=_sub,args=(loop,))
t1.start()
t2.start()
t1.join()  #t1线程执行完毕后,才继续往后走
t2.join() #t2线程执行完毕后,才继续往后走
print(number)  #0

一般python使用的都是RLock,注意死锁的情况。

  • t.setDaemon(布尔值):守护线程(必须放在start之前)。
  • t.setDaemon(True):设置为守护线程,主线程执行完毕后,子线程也自动关闭。
  • t.setDaemon(False):设置为非守护线程,主线程等待子线程,子线程执行完毕后,主线程才结束。(默认)
import threading,time
def task(arg):
    time.sleep(5)
    print('任务')

t=threading.Thread(target=task,args=(11,))
t.setDaemon(True)
t.start()

print('END')

'''
输出结果:END
'''
  • 线程名称的设置和获取
import threading

def task(arg):
    #获取当前执行此代码的线程
    name=threading.current_thread().getName()
    print(name)

for i in range(5):
    t=threading.Thread(target=task,args=(1,))
    t.setName('王腾—{}'.format(i))
    t.start()

'''
运行结果:
王腾—0
王腾—1
王腾—2
王腾—3
王腾—4
'''
  • 自定义线程类,直接将线程需要做的事情写到run方法中。
import threading

class MyThread(threading.Thread):
    def run(self):
        print('执行此线程',self._args)

t=MyThread(args=(100,))
t.start()

'''
输出结果:
执行此线程 (100,)
'''
import requests,threading

class xiazaiThread(threading.Thread):
    def run(self):
        file_name,video_url=self._args
        res=requests.get(video_url)
        with open(file_name,'wb') as f:
            f.write(res.content)

url_list=[
    ("线程:死锁.mp4","https://www.bilibili.com/video/BV1Ev411G7i3?p=7&vd_source=43a8b5ecb97eee8636fd8ac7ae8151ca"),
    ("线程:总结和作业.mp4","https://www.bilibili.com/video/BV1Ev411G7i3?p=10&vd_source=43a8b5ecb97eee8636fd8ac7ae8151ca"),
    ("进程:今日概要.mp4","https://www.bilibili.com/video/BV1Ev411G7i3?p=12&vd_source=43a8b5ecb97eee8636fd8ac7ae8151ca")
]
for item in url_list:
    t=xiazaiThread(args=(item[0],item[1]))
    t.start()

输出结果:

  •  线程池:

线程池的创建:

from concurrent.futures import ThreadPoolExecutor
pool=ThreadPoolExecutor(100)
pool.submit(函数名,参数1,参数2,参数...)
import time
from concurrent.futures import ThreadPoolExecutor

def task(vi_url,num):
    print('开始执行任务',vi_url)
    time.sleep(4)

#创建线程池,最多维护5个线程:
pool=ThreadPoolExecutor(5)
url_list=['www.xxxx_{}.com'.format(i) for i in range(11)]

for url in url_list:
    #在线程池中提交一个任务,线程池中如果有空闲线程,则分配一个线程去执行,执行完毕后再将线程返还给线程池,如果没有空闲线程,则等待。
    pool.submit(task,url,2)

print('END')
'''
运行结果:
开始执行任务 www.xxxx_0.com
开始执行任务 www.xxxx_1.com
开始执行任务 www.xxxx_2.com
开始执行任务 www.xxxx_3.com
开始执行任务 www.xxxx_4.com
END
开始执行任务开始执行任务 www.xxxx_6.com开始执行任务 www.xxxx_7.com开始执行任务 开始执行任务 
 www.xxxx_9.com

www.xxxx_5.com
www.xxxx_8.com
开始执行任务 www.xxxx_10.com
'''

ThreaLocal

进程 vs 线程

分布式进程

正则表达式

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值