Python入门教程

  • 228
  • 2022年2月27日19:50:21
  • 阅读模式

下载:https://www.python.org/downloads/

下载后选择自定义安装,自定义路径。

基础语法

数据类型及操作

文本类型: str
数字类型: int, float, complex
序列类型: list, tuple, range
映射类型: dict
套装类型: set, frozenset
布尔类型: bool
二进制类型: bytes, bytearray, memoryview

  • 内置的 type() 函数可以用来查询变量所指的对象类型;
  • 数据类型强转:类型(被转数据),例如str(3);
  • 可以在一行中为多个变量赋值;x, y, z = "一号", "二号", "三号"。
  • 可以在一行中为多个变量分配相同的值:x = y = z = "菜鸟"
  • 函数外创建的变量是全局变量,可到处使用;内部创建的是局部变量,函数内使用。同名时(一个全局一个局部)算2个不同的变量。
  • 要在函数内创建全局变量,可以使用 global关键字
  • 可以同时为多个变量赋值,如 a, b = 1, 2。
  • 一个变量可以通过赋值指向不同类型的对象。
  • 数值的除法(/)总是返回一个浮点数,要获取整数使用//操作符。
  • 混合计算时,Python 会把整型转换为浮点数。
  • 使用 del 语句可以删除一些对象引用,或者删除单个或多个对象
  • 大小比较:>,<,==

数字类型

int整形:x1 = 6 。是一个整数,正负,不带小数,长度不限。

float浮点类型:x2 = 6.5 。浮点数或“浮点数”是包含一位或多位小数的正数或负数。也可以是带有“e”的科学数字,表示 10 的幂

complex复数类型:x3 = 2j print(x3) 。复数写有“j”作为虚部。

bool布尔类型:x = True 。bool()评估任何值,并给您 True或False 作为返回。任何字符串都是True,空字符串除外。任何数字都是True,除了 0。任何列表、元组、集合和字典都是True,空的除外。

Str字符串

用单引号(' ')或双引号 (" ") 括起来,使用反斜杠 () 转义特殊字符。

字符串相加直接用+号。但是数字和字符串不能相+,可先将数字转为字符串;

使用三个引号(单双都行)将多行字符串分配给变量:

a = """从前有座山,
山里有座庙
庙里有个小和尚"""

没有字符数据类型,单个字符只是一个长度为 1 的字符串;因此字符串是数组,故取字符用数组方式取:第一个字符的位置为 0

【遍历字符串】:

for x in "chuanchuan":
  print(x)

【字符串切片】:

获取从位置 2 到位置 5 的字符(不包括在内):

b = "Hello, World!"
print(b[2:5])

获取从开始到位置 5 的字符(不包括在内):

b = "Hello, World!"
print(b[:5])

获取从位置 2 到最后的字符:

b = "Hello, World!"
print(b[2:])

从字符串末尾开始切片,就是从右往左看,不再是从左往右看。
例如:

b = "Hello, World!"
print(b[-5:-2])

list[]列表

List列表是一个有序、可变、可重复的集合。

创建列表

示例:x4 = ["apple", "banana", "cherry"]

也可以使用 list() 构造函数,列表可以包含具有相同值的项目。列表项被索引,第一项有索引[0],第二项有索引[1]

thislist = list(("apple", "banana", "cherry"))
print(thislist)

遍历列表

for i in mylist:
    print(i)

确定列表有多少项,请使用 len()函数;

列表项可以是任何数据类型;

访问列表

第一项的索引为 0,用[]访问元素;负索引-1指最后一项, -2指倒数第二项;负指数范围 mylist[-3:-1]

检查元素是否存在

要确定列表中是否存在指定的项目,请使用in关键字

更改、插入元素

#单个更改
mylist = ["川川一号", "川川二号", "川川三号","川川四号"]
mylist[0]="川川五号"

#多个更改,索引号需要用范围表示
mylist = ["川川一号", "川川二号", "川川三号","川川四号","川川五号"]
mylist[1:3]=["哈皮川川","憨批川川"]

#插入元素。insert()方法在指定的索引处插入一个项目
mylist = ["川川一号", "川川二号", "川川三号","川川四号"]
mylist.insert(2,'帅哥呀')

#末尾添加
mylist = ["川川一号", "川川二号", "川川三号","川川四号"]
mylist.append("憨批川川")

#指定位置添加
mylist = ["川川一号", "川川二号", "川川三号","川川四号"]
mylist.insert(2,'川川菜鸟')

#合并列表
#将另一个列表中的元素附加到当前列表,请使用extend()方法。
#extend()方法不一定要 列表,您也可以添加任何可迭代对象(元组、集合、字典等)。
mylist = ["川川一号", "川川二号", "川川三号","川川四号"]
mylist1 = ["川川一号", "川川二号", "川川三号","川川四号"]
mylist.extend(mylist1)

删除元素

mylist = ["川川一号", "川川二号", "川川三号","川川四号"]
#删除指定元素,remove()方法删除指定的元素
mylist.remove('川川二号')

#根据索引删除。pop()方法。不指定索引,该pop()方法将删除最后一项
mylist.pop(2)

#del删除指定的索引
del mylist[0]
#del关键字也可以完全删除列表。
del mylist

#clear()方法清空列表。该列表仍然存在,但没有内容
mylist.clear()#返回[]

遍历列表

#使用循环遍历列表
mylist = ["川川一号", "川川二号", "川川三号","川川四号"]
for i in mylist:
    print(i)

#遍历索引号
mylist = ["川川一号", "川川二号", "川川三号","川川四号"]
for i in range(len(mylist)):
    print(mylist[i])

#while循环遍历
mylist = ["川川一号", "川川二号", "川川三号","川川四号"]
i = 0
while i  len(mylist):
  print(mylist[i])
  i = i + 1

列表推导式

根据自定义规则新建列表,不必for循环检查每个元素

x for x in 旧列表 条件

#需要一个新列表,其中仅包含名称中带有字母“a”的fruits
fruits = ["apple", "banana", "cherry", "kiwi", "mango"]
newlist = [x for x in fruits if "a" in x]

#只接受小于 5 的数字
newlist = [x for x in range(10) if x  5]

#返回“orange”且不是“banana”
fruits = ["apple", "banana", "cherry", "kiwi", "mango"]
newlist = [x if x != "banana" else "orange" for x in fruits]

列表排序

#默认情况下,`sort()`方法区分大小写,所有大写字母都排在小写字母之前
thislist = ["banana", "Orange", "Kiwi", "cherry"]
thislist.sort()

#不区分大小写的排序
#想要一个不区分大小写的排序函数,使用 str.lower 作为键函数
thislist = ["banana", "Orange", "Kiwi", "cherry"]
thislist.sort(key = str.lower)

倒序

#reverse()方法反转元素的当前排序顺序。
mylist = ["川川一号", "川川二号", "川川三号","川川四号"]
mylist.reverse()

复制

#list()方法制作列表的副本
mylist = ["川川一号", "川川二号", "川川三号","川川四号"]
my = list(mylist)

列表相加

#最简单的方法之一是使用 + 运算符。
list1 = ["a", "b", "c"]
list2 = [1, 2, 3]
list3 = list1 + list2

#将 list2 中的所有项一个一个地附加到 list1 中
list1 = ["a", "b" , "c"]
list2 = [1, 2, 3]
for x in list2:
  list1.append(x)

#extend() 方法,其目的是将元素从一个列表添加到另一个列表
list1 = ["a", "b" , "c"]
list2 = [1, 2, 3]
list1.extend(list2)

tuple()元组

元组是一个有序、不可更改、允许重复的集合。元组项可以是任何数据类型

x5 = ("apple", "banana", "cherry")

元组项被索引,第一项被索引[0],第二项被索引[1]等

  • 获取长度:len(tuple)
  • 访问元组:tuple[index]
  • 遍历元组:for i in tuple: 或者 for i in range(len(tuple)): 或者 使用 While 循环 while i len(tuple):
  • 合并元组:使用+
  • 元组乘以次数:内容变成2倍:(1,2,3,4)*2-> (1,2,3,4,1,2,3,4)
  • 获取某个元素的出现次数:tuple.count(a)
  • 获取元素位置索引:index()

创建元组

#直接定义
tuple4 = ("川川一号", 34, True, 40, "帅哥")
#或者tuple()创建
tuple5 = tuple(("川川一号", "川川二号", "川川三号",'川川一号'))

元组不可更改,无法添加修改删除元素。可以转成list再改:list()再tuple()

解包元组

将值提取回变量中

fruits = ("apple", "banana", "cherry")
(green, yellow, red) = fruits
print(green)
print(yellow)
print(red)

#使用星号*
#如果变量的数量少于值的数量,您可以*在变量名中添加一个,值将作为列表分配给变量。
#将其余值分配为名为“red”的列表:
fruits = ("apple", "banana", "cherry", "strawberry", "raspberry")
(green, yellow, *red) = fruits

range范围

x = range(6

dict{}字典

dict字典是一个有序、可变、不重复的集合。字典是用大括号写的,有键和值。字典不能有两个具有相同键的项目:重复值将覆盖现有值;值可以是任何数据类型;字典可以嵌套字典

示例:

thisdict = {
  "brand": "Ford",
  "model": "Mustang",
  "year": 1964
}

从 Python 3.7 版开始,字典才是有序的

  • 可以使用键名进行引用:dict["brand"],也可以用 get()

  • 长度:len()

  • 返回所有键的列表:keys()

  • 返回所有值的列表:values()

  • 返回字典中的每个项目,作为列表中的元组:items()

  • 是否存在指定的键:in if 'name' in thisdict:

  • 更改字典某个键的值:

    thisdict.update({'name':'川川菜鸟'})thisdict = {
    "name": "川川",
    "address": "上海",
    "year": 2000
    }
    thisdict['name'] = '川川菜鸟'
    
    #或者我们使用update()
    thisdict.update({'name':'川川菜鸟'})
  • 添加键值:thisdict['age']=20 ,新的键直接写会自动加上去的;或者还是使用update:thisdict.update({'age':'20岁'})

  • 删除:指定键删元素:pop(),del();清空字典:clear();彻底删除字典:del dict

  • 遍历字典:for x in thisdict:

  • 复制字典:dict.copy()

  • 嵌套字典:

    child1 = {
    "name" : "Emil",
    "year" : 2004
    }
    child2 = {
    "name" : "Tobias",
    "year" : 2007
    }
    child3 = {
    "name" : "Linus",
    "year" : 2011
    }
    
    myfamily = {
    "child1" : child1,
    "child2" : child2,
    "child3" : child3
    }

set{}集合

Set集合是一个无序、无索引、不重复的集合。

示例:大括号:x = {"apple", "banana", "cherry"}

  • 遍历:

    for i in myset:
      print(i)
  • 检查是否存在:in

  • 添加元素:add()

  • 将一个集合1添加到另一个集合2:set2.update(set1),由于不能重复,自动去重

  • 删除元素:remove()、 或discard(),或者用 pop()自动删除最后一项(集合无索引)

  • 清空集合:clear()

  • 彻底删除:del set

  • 连接集合:union()

  • 取交集:intersection_update()

  • 保留互相独有的元素:symmetric_difference()

冻结集

x = frozenset({"apple", "banana", "cherry"})

byte字节

x = b"Hello"

bytearray字节数组

x = bytearray(5)

memoryview内存视图

x = memoryview(bytes(5))

注释

单行:# ,写在上方或者跟在后面均可。

多行:两排3个单引号或者双引号都可。

快捷键:Ctrl+/

"""
这里写多行注释
"""

for,while,if

运算符

加减法:+ -

乘除法:* /

取余数:%

除后返回整数://

幂运算:**

次方:^

赋值:=

加减等于等等:+= -=

左右移:<< >>

与:&

或:|

if…else...and...or...pass

a = 200
b = 33
if b > a:
  print("b 大于 a")
elif a == b:
  print("a 等于b")
else:
  print("a 小于 b")

and表示且

or表示或

pass关键字用于表示continue

while...break...continue

#只要 i 小于 6 就打印 i
i = 1
while i < 6:
  print(i)
  i += 1

break用于跳出循环,即使while条件为真

continue用于停止当前的迭代,继续下一个

for...break...continue

使用for循环,我们可以执行一组语句,对列表、元组、集合、字符串中的每个项目执行一次

fruits = ["apple", "banana", "cherry"]
for x in fruits:
  print(x)

break 可中断

continue进行下一个迭代

range()

range() 函数可创建一个整数列表,一般用在 for 循环中

for x in range(6):
  print(x)

函数()

类似于方法,def关键字定义

def my_function():
  print("Hello from a function")

调用函数:函数名()

参数:定义函数时,若参数数量未知,可用 * 写在参数名称前

def my_function(*kids):
  print("川川帅哥 " + kids[2])
my_function("名字", "性别", "菜鸟")

若参数类型数量都未知,可加2个**,会创建一个字典:

def my_function(**kid):
  print("它的名字是 " + kid["lname"])

my_function(fname = "菜鸟", lname = "川川")

不带参数时自动使用方法的默认值。

返回值:使用return返回值:

def my_function(x):
  return 5 * x

pass:表示暂时不操作

类、对象、init() 、自参数

类:class创建

对象:类名()。删除对象:del person

pass也可以让对象为空

class MyClass:
  x = 5
p1 = MyClass()
print(p1.x)

init() 函数:所有类都有一个名为 init() 的函数,它总是在类被初始化时执行。使用 init() 函数为对象属性赋值,或在创建对象时需要执行的其他操作。

class Person:
  def __init__(self, name, age):
    self.name = name
    self.age = age

p1 = Person("川川菜鸟", 20)

print(p1.name)
print(p1.age)

自参数:init(自参数,参数1,参数2),自参数用于调用后面的参数,随意命名,但是要位于第一位

class Person:
  def __init__(mysillyobject, name, age):
    mysillyobject.name = name
    mysillyobject.age = age

  def myfunc(abc):
    print("我的名字是 " + abc.name)

p1 = Person("川川菜鸟", 20)
p1.myfunc()

# 结果:我的名字是 川川菜鸟

属性

修改:直接点

删除:del person.name

继承、属性

语法:子类(父类),将父类作为参数给子类,表示子类继承了父类,它将继承该类的属性和方法

如果子类写了_init_,会将父类的覆盖掉,需要在里面调用父类的 init。也可以使用super(),不必写父类名称

class Person:
  def __init__(self, fname, lname):
    self.firstname = fname
    self.lastname = lname

  def printname(self):
    print(self.firstname, self.lastname)

class Student(Person):
  def __init__(self, fname, lname):
    Person.__init__(self, fname, lname) 
    #super().__init__(fname, lname)

x = Student("川川", "菜鸟")
x.printname()

添加属性

Student里添加一个graduationyear属性:

class Person:
  def __init__(self, fname, lname):
    self.firstname = fname
    self.lastname = lname

  def printname(self):
    print(self.firstname, self.lastname)

class Student(Person):
  def __init__(self, fname, lname, year):
    super().__init__(fname, lname)
    self.graduationyear = year

x = Student("川川", "菜鸟", 2021)
print(x.graduationyear)

变量

https://blog.csdn.net/helloxiaozhe/article/details/79139256

python变量一般有以下四种类型:

1、全局变量:在模块内、在所有函数外面、在class外面,这就是全局变量。
2、局部变量:在函数内、在class的方法内(未加self修饰的) ,这就是局部变量
3、静态变量:在class内的,但不在class的方法内的,这就是静态变量
4、实例变量:在class的方法内的,用self修饰的变量,这就是实例变量

全局变量和局部变量

# coding:utf-8
ahead = 'HelloWorld'  # 全局变量
showList = []  # 全局变量

def printAhead():
    print ahead

def printOther():
    city = 'beijing'  # city是局部变量
    print city + ahead

def printList():
    global showList  # global代表引用全局变量,没办法,不写的话,showList就成局部变量了
    showList.append(1)
    showList.append(2)
    print showList

printAhead()
printOther()
printList()

'''
输出结果为:
HelloWorld
beijingHelloWorld
[1, 2]
'''

全局变量的使用是为了使我们在一个类或一个函数中使用由函数返回的变量,并进行复杂的计算过程而使用。
对于一个函数的局部变量,则只在一个函数内部是可使用的,如果需跨越不同的函数或者类则需在基础函数中返回一个该值,在下一个函数中运行其方法才能获取该值进行计算;如果程序不复杂,在一个类中可以解决全局变量会为我们节省不少的时间,以及内存空间。

静态变量和实例变量:

class Person(object):

    TAG = "Person"  # 静态变量

    def __init__(self, name):   # self 当前的实例对象(简单的说一下,其实都是引用哈)
        print "__init__", Person.TAG  # 这里调用了静态变量
        self.personName = name  # personName是实例变量 (简单说就是因为self,哈哈)

    def printname(self):
        group = "BeiJing_"   # group是局部变量
        print group + self.personName  # self.personName, 调用实例变量

if __name__ == "__main__":
    p = Person("YaoMing")
    p.printname()
    print "__main__", p.TAG

'''
输出结果为:
__init__ Person
BeiJing_YaoMing
__main__ Person
'''

错误和异常

API

内置函数

自带的,不用引入外部依赖

abs() dict() help() min() setattr()
all() dir() hex() next() slice()
any() divmod() id() object() sorted()
ascii() enumerate() input() oct() staticmethod()
bin() eval() int() open() str()
bool() exec() isinstance() ord() sum()
bytearray() filter() issubclass() pow() super()
bytes() float() iter() print() tuple()
callable() format() len() property() type()
chr() frozenset() list() range() vars()
classmethod() getattr() locals() repr() zip()
compile() globals() map() reversed() import()
complex() hasattr() max() round()
delattr() hash() memoryview() set()

数据操作

1、随机数:

import random
# 1 到 10之间的一个随机数
print(random.randrange(1, 11))

2、类型转换

  • int() - 从整数文字、浮点文字(通过删除所有小数)或字符串文字(提供字符串表示整数)构造整数
  • float() - 从整数文字、浮点文字或字符串文字构造浮点数(提供字符串表示浮点数或整数)
  • str() - 从多种数据类型构造一个字符串,包括字符串、整数文字和浮点文字

3、遍历字符串

for x in "chuanchuan":
  print(x)

4、字符串的长度

len()函数返回,标点符号和空格也算一个长度

5、检查字符串中是否存在某个短语或字符

使用关键字 in;检查以下文本中是否不存在用not in

txt = "The best things in life are free!"
print("free" in txt)

6、切片返回多个字符

指定开始索引和结束索引,以冒号分隔;

#获取从位置 2 到位置 5 的字符(第5个不包括在内)
b = "Hello, World!"
print(b[2:5])

#从头开始切片:从开始到位置 5 的字符
b = "Hello, World!"
print(b[:5])

#切到最后,从位置 2 到最后的字符
b = "Hello, World!"
print(b[2:])

#用负索引从字符串末尾开始切片,就是从右往左看,最右的索引是0。打印orl
b = "Hello, World!"
print(b[-5:-2])

7、字符串大小写

lower()方法以小写形式返回字符串:

a = "Hello, World!"
print(a.lower())

upper()方法以大写形式返回字符串:

8、删除空格

strip()方法从开头或结尾删除任何空格。

a = " Hello, World! "
print(a.strip())

9、替换字符串

replace()方法用另一个字符串替换一个字符串.

a = "Hello, World!"
print(a.replace("H", "J"))

返回为Jello,World!

10、拆分字符串

split()方法返回一个列表,其中指定分隔符之间的文本成为列表项。split()如果找到分隔符的实例,该方法会将字符串拆分为子字符串。

将下面的字符串以逗号分隔:

a = "Hello, World!"
print(a.split(","))

返回一个数组,2个元素分别是'hello',' world'

11、字符串连接

要连接或组合两个字符串,您可以使用 + 运算符

12、格式化字符串

不能直接组合字符串和数字,可以通过使用format() 方法。format()方法接受传递的参数,格式化它们,并将它们放在占位符所在的字符串中 {}。format() 方法接受无限数量的参数,并放置在各自的占位符中。

#单个
age = 20
txt = "川川今年 {}"
print(txt.format(age))
结果是川川今年 20

#多个
quantity = 20
itemno = 3000
price = 49.95
myorder = "川川今年 {}岁 买了个华为手机 {} 每个月花费 {} 元."
print(myorder.format(quantity, itemno, price))

#可以使用索引号{0}来确保参数放置在正确的占位符中:
quantity = 20
itemno = 3000
price = 49.95
myorder = "川川今年 {2}岁 买了个华为手机 {0} 每个月花费 {1} 元."
print(myorder.format(quantity, itemno, price))

13、其他字符串操作:

capitalize() :将第一个字符转换为大写

casefold() :将字符串转换为小写

center() :返回一个居中的字符串

count() :返回指定值在字符串中出现的次数

encode() :返回字符串的编码版本

endswith() :如果字符串以指定的值结尾,则返回 true

join() :将可迭代的元素连接到字符串的末尾

find() :在字符串中搜索指定值并返回找到它的位置

format() :初始化字符串中的指定值

index() :在字符串中搜索指定值并返回找到它的位置

14,

日期

导入 datetime 模块并显示当前日期

import datetime

x = datetime.datetime.now()
print(x)#完整日期
print(x.year)#年份
print(x.strftime("%A"))#Monday

#创建日期,
x = datetime.datetime(2021, 8, 25)

#显示可读名称:strftime()
#显示月份名称
x = datetime.datetime(2011, 8, 25)
print(x.strftime("%B")) #August

JSON

import json,使用json.loads()方法对其进行解析JSON,返回一个字典:

import json
# some JSON:
x ='{ "name":"川川", "age":20, "city":"上海"}'

# 解析x
y = json.loads(x)
#会返回字典
print(y["age"])#20

使用json.dumps()方法将对象转换为 JSON :

import json
# x为字典
x = {
  "name": "John",
  "age": 30,
  "city": "New York"
}
# 转为json
y = json.dumps(x)
# 结果为json字符串
print(y)
print(type(y))

异常处理

由于 try 块引发错误,将执行 except 块。如果没有 try 块,程序将崩溃并引发错误:

try:
  print(x)
except:
  print("An exception occurred")

如果没有出现错误,您可以使用else关键字来定义要执行的代码块:

try:
  print("Hello")
except:
  print("Something went wrong")
else:
  print("Nothing went wrong")

如果指定了finally块,则无论 try 块是否引发错误,都将执行该块

try:
  print(x)
except:
  print("Something went wrong")
finally:
  print("The 'try except' is finished")

抛出异常:使用raise关键字:

#如果 x 小于 0,则引发错误并停止程序
x = -1
if x < 0:
  raise Exception("Sorry, no numbers below zero")

#可以定义要引发的错误类型以及要打印给用户的文本
x = "hello"
if not type(x) is int:
  raise TypeError("Only integers are allowed")

输入输出

用户输入:input()

正则表达式

注意正则表达式的写法,例如空格是\s,具体看:https://www.runoob.com/regexp/regexp-syntax.html

元字符 描述
\ 将下一个字符标记符、或一个向后引用、或一个八进制转义符。例如,"\n"匹配\n。"\n"匹配换行符。序列"\"匹配"\"而"("则匹配"("。即相当于多种编程语言中都有的"转义字符"的概念。
^ 匹配输入字行首。如果设置了RegExp对象的Multiline属性,^也匹配"\n"或"\r"之后的位置。
$ 匹配输入行尾。如果设置了RegExp对象的Multiline属性,$也匹配"\n"或"\r"之前的位置。
* 匹配前面的子表达式任意次。例如,zo能匹配"z",也能匹配"zo"以及"zoo"。等价于{0,}。
+ 匹配前面的子表达式一次或多次(大于等于1次)。例如,"zo+"能匹配"zo"以及"zoo",但不能匹配"z"。+等价于{1,}。
? 匹配前面的子表达式零次或一次。例如,"do(es)?"可以匹配"do"或"does"。?等价于{0,1}。
{n} n是一个非负整数。匹配确定的n次。例如,"o{2}"不能匹配"Bob"中的"o",但是能匹配"food"中的两个o。
{n,} n是一个非负整数。至少匹配n次。例如,"o{2,}"不能匹配"Bob"中的"o",但能匹配"foooood"中的所有o。"o{1,}"等价于"o+"。"o{0,}"则等价于"o*"。
{n,m} mn均为非负整数,其中n<=m。最少匹配n次且最多匹配m次。例如,"o{1,3}"将匹配"fooooood"中的前三个o为一组,后三个o为一组。"o{0,1}"等价于"o?"。请注意在逗号和两个数之间不能有空格。
? 当该字符紧跟在任何一个其他限制符(,+,?,{n},{n,},{n,m*})后面时,匹配模式是非贪婪的。非贪婪模式尽可能少地匹配所搜索的字符串,而默认的贪婪模式则尽可能多地匹配所搜索的字符串。例如,对于字符串"oooo","o+"将尽可能多地匹配"o",得到结果["oooo"],而"o+?"将尽可能少地匹配"o",得到结果 ['o', 'o', 'o', 'o']
.点 匹配除"\n"和"\r"之外的任何单个字符。要匹配包括"\n"和"\r"在内的任何字符,请使用像"[\s\S]"的模式。
(pattern) 匹配pattern并获取这一匹配。所获取的匹配可以从产生的Matches集合得到,在VBScript中使用SubMatches集合,在JScript中则使用$0…$9属性。要匹配圆括号字符,请使用"("或")"。
(?:pattern) 非获取匹配,匹配pattern但不获取匹配结果,不进行存储供以后使用。这在使用或字符"(|)"来组合一个模式的各个部分时很有用。例如"industr(?:y|ies)"就是一个比"industry|industries"更简略的表达式。
(?=pattern) 非获取匹配,正向肯定预查,在任何匹配pattern的字符串开始处匹配查找字符串,该匹配不需要获取供以后使用。例如,"Windows(?=95|98|NT|2000)"能匹配"Windows2000"中的"Windows",但不能匹配"Windows3.1"中的"Windows"。预查不消耗字符,也就是说,在一个匹配发生后,在最后一次匹配之后立即开始下一次匹配的搜索,而不是从包含预查的字符之后开始。
(?!pattern) 非获取匹配,正向否定预查,在任何不匹配pattern的字符串开始处匹配查找字符串,该匹配不需要获取供以后使用。例如"Windows(?!95|98|NT|2000)"能匹配"Windows3.1"中的"Windows",但不能匹配"Windows2000"中的"Windows"。
(?<=pattern) 非获取匹配,反向肯定预查,与正向肯定预查类似,只是方向相反。例如,"(?<=95|98|NT|2000)Windows"能匹配"2000Windows"中的"Windows",但不能匹配"3.1Windows"中的"Windows"。*python的正则表达式没有完全按照正则表达式规范实现,所以一些高级特性建议使用其他语言如java、scala等
(?<!patte_n) 非获取匹配,反向否定预查,与正向否定预查类似,只是方向相反。例如"(?<!95|98|NT|2000)Windows"能匹配"3.1Windows"中的"Windows",但不能匹配"2000Windows"中的"Windows"。*python的正则表达式没有完全按照正则表达式规范实现,所以一些高级特性建议使用其他语言如java、scala等
x|y 匹配x或y。例如,"z|food"能匹配"z"或"food"(此处请谨慎)。"[z|f]ood"则匹配"zood"或"food"。
[xyz] 字符集合。匹配所包含的任意一个字符。例如,"[abc]"可以匹配"plain"中的"a"。
[^xyz] 负值字符集合。匹配未包含的任意字符。例如,"[^abc]"可以匹配"plain"中的"plin"任一字符。
[a-z] 字符范围。匹配指定范围内的任意字符。例如,"[a-z]"可以匹配"a"到"z"范围内的任意小写字母字符。注意:只有连字符在字符组内部时,并且出现在两个字符之间时,才能表示字符的范围; 如果出字符组的开头,则只能表示连字符本身.
[^a-z] 负值字符范围。匹配任何不在指定范围内的任意字符。例如,"[^a-z]"可以匹配任何不在"a"到"z"范围内的任意字符。
\b 匹配一个单词的边界,也就是指单词和空格间的位置(即正则表达式的"匹配"有两种概念,一种是匹配字符,一种是匹配位置,这里的\b就是匹配位置的)。例如,"er\b"可以匹配"never"中的"er",但不能匹配"verb"中的"er";"\b1_"可以匹配"123"中的"1",但不能匹配"213"中的"1"。
\B 匹配非单词边界。"er\B"能匹配"verb"中的"er",但不能匹配"never"中的"er"。
\cx 匹配由x指明的控制字符。例如,\cM匹配一个Control-M或回车符。x的值必须为A-Z或a-z之一。否则,将c视为一个原义的"c"字符。
\d 匹配一个数字字符。等价于[0-9]。grep 要加上-P,perl正则支持
\D 匹配一个非数字字符。等价于[^0-9]。grep要加上-P,perl正则支持
\f 匹配一个换页符。等价于\x0c和\cL。
\n 匹配一个换行符。等价于\x0a和\cJ。
\r 匹配一个回车符。等价于\x0d和\cM。
\s 匹配任何不可见字符,包括空格、制表符、换页符等等。等价于[ \f\n\r\t\v]。
\S 匹配任何可见字符。等价于[^ \f\n\r\t\v]。
\t 匹配一个制表符。等价于\x09和\cI。
\v 匹配一个垂直制表符。等价于\x0b和\cK。
\w 匹配包括下划线的任何单词字符。类似但不等价于"[A-Za-z0-9_]",这里的"单词"字符使用Unicode字符集。
\W 匹配任何非单词字符。等价于"[^A-Za-z0-9_]"。
\xn 匹配n,其中n为十六进制转义值。十六进制转义值必须为确定的两个数字长。例如,"\x41"匹配"A"。"\x041"则等价于"\x04&1"。正则表达式中可以使用ASCII编码。
*num* 匹配num,其中num是一个正整数。对所获取的匹配的引用。例如,"(.)\1"匹配两个连续的相同字符。
*n* 标识一个八进制转义值或一个向后引用。如果*n之前至少n个获取的子表达式,则n为向后引用。否则,如果n为八进制数字(0-7),则n*为一个八进制转义值。
*nm* 标识一个八进制转义值或一个向后引用。如果*nm之前至少有nm个获得子表达式,则nm为向后引用。如果*nm之前至少有n个获取,则n为一个后跟文字m的向后引用。如果前面的条件都不满足,若nm均为八进制数字(0-7),则*nm将匹配八进制转义值nm*。
*nml* 如果n为八进制数字(0-7),且ml均为八进制数字(0-7),则匹配八进制转义值nml
\un 匹配n,其中n是一个用四个十六进制数字表示的Unicode字符。例如,\u00A9匹配版权符号(©)。
\p{P} 小写 p 是 property 的意思,表示 Unicode 属性,用于 Unicode 正表达式的前缀。中括号内的"P"表示Unicode 字符集七个字符属性之一:标点字符。其他六个属性:L:字母;M:标记符号(一般不会单独出现);Z:分隔符(比如空格、换行等);S:符号(比如数学符号、货币符号等);N:数字(比如阿拉伯数字、罗马数字等);C:其他字符。*注:此语法部分语言不支持,例:javascript。
\<> 匹配词(word)的开始(\<)和结束(>)。例如正则表达式\<the>能够匹配字符串"for the wise"中的"the",但是不能匹配字符串"otherwise"中的"the"。注意:这个元字符不是所有的软件都支持的。
( ) 将( 和 ) 之间的表达式定义为"组"(group),并且将匹配这个表达式的字符保存到一个临时区域(一个正则表达式中最多可以保存9个),它们可以用 \1 到\9 的符号来引用。
| 将两个匹配条件进行逻辑"或"(or)运算。例如正则表达式(him|her) 匹配"it belongs to him"和"it belongs to her",但是不能匹配"it belongs to them."。注意:这个元字符不是所有的软件都支持的。

首先要导入re模块,re包使用见 https://www.cnblogs.com/hanmk/p/9143514.html

re.findAll():在字符串中找到正则表达式所匹配的所有子串,并返回一个列表,如果有多个匹配模式,则返回元组列表,如果没有找到匹配的,则返回空列表

re.findall(pattern, string, flags=0) 或者
pattern.findall(string[, pos[, endpos]])

  • pattern 匹配模式。
  • string 待匹配的字符串。
  • pos 可选参数,指定字符串的起始位置,默认为 0。
  • endpos 可选参数,指定字符串的结束位置,默认为字符串的长度。
import re

result1 = re.findall(r'\d+','runoob 123 google 456')

pattern = re.compile(r'\d+')   # 查找数字
result2 = pattern.findall('runoob 123 google 456')
result3 = pattern.findall('run88oob123google456', 0, 10)

print(result1)
print(result2)
print(result3)

['123', '456']
['123', '456']
['88', '12']

re.search():re.search 扫描整个字符串并返回第一个成功的匹配。参数:匹配表达式,要匹配的字符串,标志位

import re
print(re.search('www', 'www.runoob.com').span())  # 在起始位置匹配
print(re.search('com', 'www.runoob.com').span())         # 不在起始位置匹配

re.split():拆分字符串,匹配拆分。

re.split(pattern, string[, maxsplit=0, flags=0])

  • pattern:匹配的字符串
  • string:需要切分的字符串
  • maxsplit:分隔次数,默认为0(即不限次数)
  • flags:标志位,用于控制正则表达式的匹配方式,比如:是否区分大小写,,,如下图所示
import re

txt = "菜鸟 学 python"
x = re.split("\s", txt)
print(x)#['菜鸟','学','python']

#可指定拆分次数,例如

re.sub():用于替换字符串

re.sub(pattern, repl, string, count=0, flags=0)

  • pattern : 正则中的模式字符串。
  • repl : 替换的字符串,也可为一个函数。
  • string : 要被查找替换的原始字符串。
  • count : 模式匹配后替换的最大次数,默认 0 表示替换所有的匹配。
  • flags : 编译时用的匹配模式,数字形式。

前三个为必选参数,后两个为可选参数

import re

phone = "2004-959-559 # 这是一个电话号码"

# 删除注释
num = re.sub(r'#.*$', "", phone)
print ("电话号码 : ", num)

# 移除非数字的内容
num = re.sub(r'\D', "", phone)
print ("电话号码 : ", num)

电话号码 :  2004-959-559 
电话号码 :  2004959559

compile():compile 函数用于编译正则表达式,生成一个正则表达式( Pattern )对象,然后就可以用编译后的正则表达式去匹配字符串

操作MySQL

有很多依赖,例如mysql-connector、pymysql等,这里推荐后者。

pymysql

写法:

import pymysql

# 打开数据库连接
db = pymysql.connect(host='localhost',
                     user='root',
                     password='test123',
                     database='TESTDB')

# 使用 cursor() 方法创建一个游标对象 cursor
cursor = db.cursor()

# 使用 execute() 方法执行 SQL,如果表存在则删除
cursor.execute("DROP TABLE IF EXISTS EMPLOYEE")

# 使用预处理语句创建表
sql = """CREATE TABLE EMPLOYEE (
         FIRST_NAME  CHAR(20) NOT NULL,
         LAST_NAME  CHAR(20),
         AGE INT,  
         SEX CHAR(1),
         INCOME FLOAT )"""

cursor.execute(sql)

# 关闭数据库连接
db.close()
创建表
import pymysql

# 打开数据库连接
db = pymysql.connect(host='localhost',
                     user='testuser',
                     password='test123',
                     database='TESTDB')

# 使用 cursor() 方法创建一个游标对象 cursor
cursor = db.cursor()

# 使用 execute() 方法执行 SQL,如果表存在则删除
cursor.execute("DROP TABLE IF EXISTS EMPLOYEE")

# 使用预处理语句创建表
sql = """CREATE TABLE EMPLOYEE (
         FIRST_NAME  CHAR(20) NOT NULL,
         LAST_NAME  CHAR(20),
         AGE INT,  
         SEX CHAR(1),
         INCOME FLOAT )"""

cursor.execute(sql)

# 关闭数据库连接
db.close()
插入记录
import pymysql

# 打开数据库连接
db = pymysql.connect(host='localhost',
                     user='testuser',
                     password='test123',
                     database='TESTDB')

# 使用cursor()方法获取操作游标 
cursor = db.cursor()

# SQL 插入语句
sql = """INSERT INTO EMPLOYEE(FIRST_NAME,
         LAST_NAME, AGE, SEX, INCOME)
         VALUES ('Mac', 'Mohan', 20, 'M', 2000)"""
try:
   # 执行sql语句
   cursor.execute(sql)
   # 提交到数据库执行
   db.commit()
except:
   # 如果发生错误则回滚
   db.rollback()

# 关闭数据库连接
db.close()
查询记录

查询Mysql使用 fetchone() 方法获取单条数据, 使用fetchall() 方法获取多条数据。

  • fetchone(): 该方法获取下一个查询结果集。结果集是一个对象
  • fetchall(): 接收全部的返回结果行.
  • rowcount: 这是一个只读属性,并返回执行execute()方法后影响的行数。

查询EMPLOYEE表中salary(工资)字段大于1000的所有数据:

import pymysql

# 打开数据库连接
db = pymysql.connect(host='localhost',
                     user='testuser',
                     password='test123',
                     database='TESTDB')

# 使用cursor()方法获取操作游标 
cursor = db.cursor()

# SQL 查询语句
sql = "SELECT * FROM EMPLOYEE \
       WHERE INCOME > %s" % (1000)
try:
   # 执行SQL语句
   cursor.execute(sql)
   # 获取所有记录列表
   results = cursor.fetchall()
   for row in results:
      fname = row[0]
      lname = row[1]
      age = row[2]
      sex = row[3]
      income = row[4]
       # 打印结果
      print ("fname=%s,lname=%s,age=%s,sex=%s,income=%s" % \
             (fname, lname, age, sex, income ))
except:
   print ("Error: unable to fetch data")

# 关闭数据库连接
db.close()

#结果:fname=Mac, lname=Mohan, age=20, sex=M, income=2000
更新记录

将 TESTDB 表中 SEX 为 'M' 的 AGE 字段递增 1:

import pymysql

# 打开数据库连接
db = pymysql.connect(host='localhost',
                     user='testuser',
                     password='test123',
                     database='TESTDB')

# 使用cursor()方法获取操作游标 
cursor = db.cursor()

# SQL 更新语句
sql = "UPDATE EMPLOYEE SET AGE = AGE + 1 WHERE SEX = '%c'" % ('M')
try:
   # 执行SQL语句
   cursor.execute(sql)
   # 提交到数据库执行
   db.commit()
except:
   # 发生错误时回滚
   db.rollback()

# 关闭数据库连接
db.close()
删除

删除数据表 EMPLOYEE 中 AGE 大于 20 的所有数据:

import pymysql

# 打开数据库连接
db = pymysql.connect(host='localhost',
                     user='testuser',
                     password='test123',
                     database='TESTDB')

# 使用cursor()方法获取操作游标 
cursor = db.cursor()

# SQL 删除语句
sql = "DELETE FROM EMPLOYEE WHERE AGE > %s" % (20)
try:
   # 执行SQL语句
   cursor.execute(sql)
   # 提交修改
   db.commit()
except:
   # 发生错误时回滚
   db.rollback()

# 关闭连接
db.close()
事务
  • 原子性(atomicity)。一个事务是一个不可分割的工作单位,事务中包括的诸操作要么都做,要么都不做。
  • 一致性(consistency)。事务必须是使数据库从一个一致性状态变到另一个一致性状态。一致性与原子性是密切相关的。
  • 隔离性(isolation)。一个事务的执行不能被其他事务干扰。即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。
  • 持久性(durability)。持续性也称永久性(permanence),指一个事务一旦提交,它对数据库中数据的改变就应该是永久性的。接下来的其他操作或故障不应该对其有任何影响。

当游标建立之时,就自动开始了一个隐形的数据库事务。

commit()方法游标的所有更新操作,rollback()方法回滚当前游标的所有操作。每一个方法都开始了一个新的事务。

示例:

# SQL删除记录语句
sql = "DELETE FROM EMPLOYEE WHERE AGE > %s" % (20)
try:
   # 执行SQL语句
   cursor.execute(sql)
   # 向数据库提交
   db.commit()
except:
   # 发生错误时回滚
   db.rollback()
错误处理
异常 描述
Warning 当有严重警告时触发,例如插入数据是被截断等等。必须是 StandardError 的子类。
Error 警告以外所有其他错误类。必须是 StandardError 的子类。
InterfaceError 当有数据库接口模块本身的错误(而不是数据库的错误)发生时触发。 必须是Error的子类。
DatabaseError 和数据库有关的错误发生时触发。 必须是Error的子类。
DataError 当有数据处理时的错误发生时触发,例如:除零错误,数据超范围等等。 必须是DatabaseError的子类。
OperationalError 指非用户控制的,而是操作数据库时发生的错误。例如:连接意外断开、 数据库名未找到、事务处理失败、内存分配错误等等操作数据库是发生的错误。 必须是DatabaseError的子类。
IntegrityError 完整性相关的错误,例如外键检查失败等。必须是DatabaseError子类。
InternalError 数据库的内部错误,例如游标(cursor)失效了、事务同步失败等等。 必须是DatabaseError子类。
ProgrammingError 程序错误,例如数据表(table)没找到或已存在、SQL语句语法错误、 参数数量错误等等。必须是DatabaseError的子类。
NotSupportedError 不支持错误,指使用了数据库不支持的函数或API等。例如在连接对象上 使用.rollback()函数,然而数据库并不支持事务或者事务已关闭。 必须是DatabaseError的子类。

Socket

socket.socket([family[, type[, proto]]])

  • family: 套接字家族可以是 AF_UNIX 或者 AF_INET
  • type: 套接字类型可以根据是面向连接的还是非连接分为SOCK_STREAMSOCK_DGRAM
  • protocol: 一般不填默认为0.

内置函数

函数 描述
服务器端套接字
s.bind() 绑定地址(host,port)到套接字, 在AF_INET下,以元组(host,port)的形式表示地址。
s.listen() 开始TCP监听。backlog指定在拒绝连接之前,操作系统可以挂起的最大连接数量。该值至少为1,大部分应用程序设为5就可以了。
s.accept() 被动接受TCP客户端连接,(阻塞式)等待连接的到来
客户端套接字
s.connect() 主动初始化TCP服务器连接,。一般address的格式为元组(hostname,port),如果连接出错,返回socket.error错误。
s.connect_ex() connect()函数的扩展版本,出错时返回出错码,而不是抛出异常
公共用途的套接字函数
s.recv() 接收TCP数据,数据以字符串形式返回,bufsize指定要接收的最大数据量。flag提供有关消息的其他信息,通常可以忽略。
s.send() 发送TCP数据,将string中的数据发送到连接的套接字。返回值是要发送的字节数量,该数量可能小于string的字节大小。
s.sendall() 完整发送TCP数据,完整发送TCP数据。将string中的数据发送到连接的套接字,但在返回之前会尝试发送所有数据。成功返回None,失败则抛出异常。
s.recvfrom() 接收UDP数据,与recv()类似,但返回值是(data,address)。其中data是包含接收数据的字符串,address是发送数据的套接字地址。
s.sendto() 发送UDP数据,将数据发送到套接字,address是形式为(ipaddr,port)的元组,指定远程地址。返回值是发送的字节数。
s.close() 关闭套接字
s.getpeername() 返回连接套接字的远程地址。返回值通常是元组(ipaddr,port)。
s.getsockname() 返回套接字自己的地址。通常是一个元组(ipaddr,port)
s.setsockopt(level,optname,value) 设置给定套接字选项的值。
s.getsockopt(level,optname[.buflen]) 返回套接字选项的值。
s.settimeout(timeout) 设置套接字操作的超时期,timeout是一个浮点数,单位是秒。值为None表示没有超时期。一般,超时期应该在刚创建套接字时设置,因为它们可能用于连接的操作(如connect())
s.gettimeout() 返回当前超时期的值,单位是秒,如果没有设置超时期,则返回None。
s.fileno() 返回套接字的文件描述符。
s.setblocking(flag) 如果 flag 为 False,则将套接字设为非阻塞模式,否则将套接字设为阻塞模式(默认值)。非阻塞模式下,如果调用 recv() 没有发现任何数据,或 send() 调用无法立即发送数据,那么将引起 socket.error 异常。
s.makefile() 创建一个与该套接字相关连的文件

服务端示例

我们使用 socket 模块的 socket 函数来创建一个 socket 对象。socket 对象可以通过调用其他函数来设置一个 socket 服务。

现在我们可以通过调用 bind(hostname, port) 函数来指定服务的 port(端口)

接着,我们调用 socket 对象的 accept 方法。该方法等待客户端的连接,并返回 connection 对象,表示已连接到客户端。

# 导入 socket、sys 模块
import socket
import sys

# 创建 socket 对象
serversocket = socket.socket(
            socket.AF_INET, socket.SOCK_STREAM)

# 获取本地主机名
host = socket.gethostname()

port = 9999

# 绑定端口号
serversocket.bind((host, port))

# 设置最大连接数,超过后排队
serversocket.listen(5)

while True:
    # 建立客户端连接
    clientsocket,addr = serversocket.accept()      

    print("连接地址: %s" % str(addr))

    msg='欢迎访问菜鸟教程!'+ "\r\n"
    clientsocket.send(msg.encode('utf-8'))
    clientsocket.close()

客户端示例

# 导入 socket、sys 模块
import socket
import sys

# 创建 socket 对象
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# 获取本地主机名
host = socket.gethostname()

# 设置端口号
port = 9999

# 连接服务,指定主机和端口
s.connect((host, port))

# 接收小于 1024 字节的数据
msg = s.recv(1024)

s.close()

print (msg.decode('utf-8'))

现在我们打开两个终端,第一个终端执行 server.py 文件:

$ python3 server.py

第二个终端执行 client.py 文件:

$ python3 client.py 
欢迎访问菜鸟教程!

这时我们再打开第一个终端,就会看到有以下信息输出:

连接地址: ('192.168.0.118', 33397)

多线程

创建

  • 使用线程可以把占据长时间的程序中的任务放到后台去处理。
  • 用户界面可以更加吸引人,比如用户点击了一个按钮去触发某些事件的处理,可以弹出一个进度条来显示处理的进度。
  • 程序的运行速度可能加快。
  • 在一些等待的任务实现上如用户输入、文件读写和网络收发数据等,线程就比较有用了。在这种情况下我们可以释放一些珍贵的资源如内存占用等等。

线程可以分为:

  • 内核线程:由操作系统内核创建和撤销。
  • 用户线程:不需要内核支持而在用户程序中实现的线程。

Python3 线程中常用的两个模块为:

  • _thread( Python3 中不能再使用"thread" 模块)
  • threading(推荐使用)

使用线程有两种方式:函数或者用类来包装线程对象。

threading模块:

  • threading.currentThread(): 返回当前的线程变量。
  • threading.enumerate(): 返回一个包含正在运行的线程的list。正在运行指线程启动后、结束前,不包括启动前和终止后的线程。
  • threading.activeCount(): 返回正在运行的线程数量,与len(threading.enumerate())有相同的结果。

线程模块同样提供了Thread类来处理线程,Thread类提供了以下方法:

  • run(): 用以表示线程活动的方法。
  • start(): 启动线程活动。
  • join([time]): 等待至线程中止。这阻塞调用线程直至线程的join() 方法被调用中止-正常退出或者抛出未处理的异常-或者是可选的超时发生。
  • isAlive(): 返回线程是否活动的。
  • getName(): 返回线程名。
  • setName(): 设置线程名。

使用 threading 模块创建线程:

import threading
import time

exitFlag = 0

#直接从 threading.Thread 继承创建一个新的子类,并实例化后调用 start() 方法启动新线程,即它调用了线程的 run() 方法:
class myThread (threading.Thread):
    def __init__(self, threadID, name, delay):
        threading.Thread.__init__(self)
        self.threadID = threadID
        self.name = name
        self.delay = delay
    def run(self):
        print ("开始线程:" + self.name)
        print_time(self.name, self.delay, 5)
        print ("退出线程:" + self.name)

def print_time(threadName, delay, counter):
    while counter:
        if exitFlag:
            threadName.exit()
        time.sleep(delay)
        print ("%s: %s" % (threadName, time.ctime(time.time())))
        counter -= 1

# 创建新线程
thread1 = myThread(1, "Thread-1", 1)
thread2 = myThread(2, "Thread-2", 2)

# 开启新线程
thread1.start()
thread2.start()
thread1.join()
thread2.join()
print ("退出主线程")

#结果
开始线程:Thread-1
开始线程:Thread-2
Thread-1: Wed Jan  5 17:34:54 2022
Thread-2: Wed Jan  5 17:34:55 2022
Thread-1: Wed Jan  5 17:34:55 2022
Thread-1: Wed Jan  5 17:34:56 2022
Thread-2: Wed Jan  5 17:34:57 2022
Thread-1: Wed Jan  5 17:34:57 2022
Thread-1: Wed Jan  5 17:34:58 2022
退出线程:Thread-1
Thread-2: Wed Jan  5 17:34:59 2022
Thread-2: Wed Jan  5 17:35:01 2022
Thread-2: Wed Jan  5 17:35:03 2022
退出线程:Thread-2
退出主线程

同步

如果多个线程共同对某个数据修改,则可能出现不可预料的结果,为了保证数据的正确性,需要对多个线程进行同步。

使用 Thread 对象的 Lock 和 Rlock 可以实现简单的线程同步,这两个对象都有 acquire 方法和 release 方法,对于那些需要每次只允许一个线程操作的数据,可以将其操作放到 acquire 和 release 方法之间。

考虑这样一种情况:一个列表里所有元素都是0,线程"set"从后向前把所有元素改成1,而线程"print"负责从前往后读取列表并打印。

那么,可能线程"set"开始改的时候,线程"print"便来打印列表了,输出就成了一半0一半1,这就是数据的不同步。为了避免这种情况,引入了锁的概念。

锁有两种状态——锁定和未锁定。每当一个线程比如"set"要访问共享数据时,必须先获得锁定;如果已经有别的线程比如"print"获得锁定了,那么就让线程"set"暂停,也就是同步阻塞;等到线程"print"访问完毕,释放锁以后,再让线程"set"继续。

经过这样的处理,打印列表时要么全部输出0,要么全部输出1,不会再出现一半0一半1的尴尬场面。

import threading
import time

class myThread (threading.Thread):
    def __init__(self, threadID, name, delay):
        threading.Thread.__init__(self)
        self.threadID = threadID
        self.name = name
        self.delay = delay
    def run(self):
        print ("开启线程: " + self.name)
        # 获取锁,用于线程同步
        threadLock.acquire()
        print_time(self.name, self.delay, 3)
        # 释放锁,开启下一个线程
        threadLock.release()

def print_time(threadName, delay, counter):
    while counter:
        time.sleep(delay)
        print ("%s: %s" % (threadName, time.ctime(time.time())))
        counter -= 1

threadLock = threading.Lock()
threads = []

# 创建新线程
thread1 = myThread(1, "Thread-1", 1)
thread2 = myThread(2, "Thread-2", 2)

# 开启新线程
thread1.start()
thread2.start()

# 添加线程到线程列表
threads.append(thread1)
threads.append(thread2)

# 等待所有线程完成
for t in threads:
    t.join()
print ("退出主线程")

#结构
开启线程: Thread-1
开启线程: Thread-2
Thread-1: Wed Jan  5 17:36:50 2022
Thread-1: Wed Jan  5 17:36:51 2022
Thread-1: Wed Jan  5 17:36:52 2022
Thread-2: Wed Jan  5 17:36:54 2022
Thread-2: Wed Jan  5 17:36:56 2022
Thread-2: Wed Jan  5 17:36:58 2022
退出主线程

线程优先级队列

Queue 模块中提供了同步的、线程安全的队列类,包括FIFO(先入先出)队列Queue,LIFO(后入先出)队列LifoQueue,和优先级队列 PriorityQueue

这些队列都实现了锁原语,能够在多线程中直接使用,可以使用队列来实现线程间的同步。

Queue 模块中的常用方法:

  • Queue.qsize() 返回队列的大小
  • Queue.empty() 如果队列为空,返回True,反之False
  • Queue.full() 如果队列满了,返回True,反之False
  • Queue.full 与 maxsize 大小对应
  • Queue.get([block[, timeout]])获取队列,timeout等待时间
  • Queue.get_nowait() 相当Queue.get(False)
  • Queue.put(item) 写入队列,timeout等待时间
  • Queue.put_nowait(item) 相当Queue.put(item, False)
  • Queue.task_done() 在完成一项工作之后,Queue.task_done()函数向任务已经完成的队列发送一个信号
  • Queue.join() 实际上意味着等到队列为空,再执行别的操作

示例:

import queue
import threading
import time

exitFlag = 0

class myThread (threading.Thread):
    def __init__(self, threadID, name, q):
        threading.Thread.__init__(self)
        self.threadID = threadID
        self.name = name
        self.q = q
    def run(self):
        print ("开启线程:" + self.name)
        process_data(self.name, self.q)
        print ("退出线程:" + self.name)

def process_data(threadName, q):
    while not exitFlag:
        queueLock.acquire()
        if not workQueue.empty():
            data = q.get()
            queueLock.release()
            print ("%s processing %s" % (threadName, data))
        else:
            queueLock.release()
        time.sleep(1)

threadList = ["Thread-1", "Thread-2", "Thread-3"]
nameList = ["One", "Two", "Three", "Four", "Five"]
queueLock = threading.Lock()
workQueue = queue.Queue(10)
threads = []
threadID = 1

# 创建新线程
for tName in threadList:
    thread = myThread(threadID, tName, workQueue)
    thread.start()
    threads.append(thread)
    threadID += 1

# 填充队列
queueLock.acquire()
for word in nameList:
    workQueue.put(word)
queueLock.release()

# 等待队列清空
while not workQueue.empty():
    pass

# 通知线程是时候退出
exitFlag = 1

# 等待所有线程完成
for t in threads:
    t.join()
print ("退出主线程")

#结果
开启线程:Thread-1
开启线程:Thread-2
开启线程:Thread-3
Thread-3 processing One
Thread-1 processing Two
Thread-2 processing Three
Thread-3 processing Four
Thread-1 processing Five
退出线程:Thread-3
退出线程:Thread-2
退出线程:Thread-1
退出主线程

文件操作

Python的文件操作涉及对文件的读/写与编码的处理,是学习爬虫的必备知识。使用Python来读/写文本需要用到“open”这个关键字。它的作用是打开一个文件,并创建一个文件对象。

流程:打开文件——读/写文件

打开文件

方式1(不推荐)

f = open(’文件路径’, ’文件操作方式’, encoding='utf-8')
对文件进行操作
f.close()

方式2(推荐):

with open(’文件路径’, ’文件操作方式’, encoding='utf-8') as f:
  对文件进行操作

第二种方法不需要手动关闭文件,只要代码退出了缩进,Python就会自动关闭文件。

读文件

使用Python打开一个文本文件时,首先要保证这个文件是存在的。在读文件的时候,“文件操作方式”这个参数可以省略,也可以写成“r”,也就是read的首字母。

文件路径可以是绝对路径,也可以是相对路径。

如果是绝对路径,Linux和Mac OS不能直接使用“~”表示“home目录”,因为Python不认识“~”这个符号。如果非要使用这个符号,需要使用Python的“os”模块,代码如下:这样,Python就会将这种风格的路径转化为Python能认识的绝对路径。

import os
real_path = os.path.expanduser('~/project/xxx')

相对路径是文本文件相对于现在的工作区而言的路径,并不总是相对于当前正在运行的这个Python文件的路径。假如打开一个同级txt文件:

#  “encoding”这个参数可以在打开文件的时候将文件转换为UTF-8编码格式,从而避免乱码的出现。这个参数只有Python 3有,在Python 2中使用这个参数会报错
with open('text.txt', encoding='utf-8') as f:
  通过f来读文件

file 对象使用 open 函数来创建,下表列出了 file 对象常用的函数:

序号 方法及描述
1 file.close()关闭文件。关闭后文件不能再进行读写操作。
2 file.flush()刷新文件内部缓冲,直接把内部缓冲区的数据立刻写入文件, 而不是被动的等待输出缓冲区写入。
3 file.fileno()返回一个整型的文件描述符(file descriptor FD 整型), 可以用在如os模块的read方法等一些底层操作上。
4 file.isatty()如果文件连接到一个终端设备返回 True,否则返回 False。
5 file.next()Python 3 中的 File 对象不支持 next() 方法。返回文件下一行。
6 file.read(size)从文件读取指定的字节数,如果未给定或为负则读取所有:直接把文件里面的全部内容用一个字符串返回
7 file.readline(size)读取整行,包括 "\n" 字符。
8 file.readlines(sizeint)读取所有行并返回列表,若给定sizeint>0,返回总和大约为sizeint字节的行, 实际读取值可能比 sizeint 较大, 因为需要填充缓冲区。
9 file.seek(offset, whence)移动文件读取指针到指定位置
10 file.tell()返回文件当前位置。
11 file.truncate(size)从文件的首行首字符开始截断,截断文件为 size 个字符,无 size 表示从当前位置截断;截断之后后面的所有字符被删除,其中 windows 系统下的换行代表2个字符大小。
12 file.write(str)将字符串写入文件,返回的是写入的字符长度。
13 file.writelines(sequence)向文件写入一个序列字符串列表,如果需要换行则要自己加入每行的换行符。

写文件

使用Python写文件也需要先打开文件:

    with open('new.txt', 'w', encoding='utf-8') as f:
      通过f来写文件

w是英文write的首字母,意思是以写的方式打开文件。这个参数除了为“w”外,还可以为“a”。它们的区别在于,如果原来已经有一个new.txt文件了,使用“w”会覆盖原来的文件,导致原来的内容丢失;而使用“a”,则会把新的内容写到原来的文件末尾。

写文件时可以直接写一大段文本,也可以写一个列表。

直接将一大段字符串写入到文本中:

    f.write("一大段文字")

把列表里面的所有字符串写入到文本中:

    f.writelines([’第一段话’, ’第二段话’, ’第三段话’])

特别注意,写列表的时候,Python写到文本中的文字是不会自动换行的,需要人工输入换行符才可以

CSV文件

CSV文件可以用Excel或者Numbers打开,得到可读性很高的表格。Python自带操作CSV的模块:import csv。使用这个模块,可以将CSV文件的内容转换为Python的字典,从而方便使用。

读CSV

csv.DictReader()

   import csv

    with open('result.csv', encoding='utf-8') as f:
      reader = csv.DictReader(f)  #列表推导式: reader = [x for x in csv.DictReader(f)]
      for row in reader: #读取文本内容的代码必须放在缩进内部进行;列表推导式可以避免这个限制
        print(row)

for循环得到的row是OrderedDict(有序字典),可以直接像普通字典那样使用:

    username = row['username']
    content = row['content']
    reply_time = row['reply_time']

写CSV

把一个字典写成CSV文件,或者把一个包含字典的列表写成CSV文件。要指定列名。列名要和字典的Key一一对应。

csv.DictWriter(): 接收两个参数:第1个参数是文件对象f;第2个参数名为fieldnames,值为字典的Key列表。

  • 写入CSV文件的列名行: writer.writeheader()
  • 将包含字典的列表全部写入到CSV文件中:writer.writerows(包含字典的列表)

写入一个包含字典的列表,每一个字典对应CSV的一行。这些字典的Key必须和fieldnames相同。字典可以是普通的无序字典,所以不需要关心字典里面Key的顺序,但是不能存在fieldnames里面没有的Key,也不能缺少fieldnames里面已有的Key。

  • 写入单个字典:writer.writerow(字典)

爬虫

半自动爬虫:手动获取网页源码,进行过滤

自动爬虫:自动获取网页源码

HTTP请求库用三方的requests。不用自带的urllib(用起来麻烦)

安装:

pip install requests
import requests

编码规范

  • 头部:大部分.py文件不必以#!作为文件的开始,只有被直接执行的文件中才有必要加入#!,程序的main文件应该以 #!/usr/bin/python2或者 #!/usr/bin/python3开始。

  • 编码:# -- coding:utf-8 --

  • 版权

    ################################################ 
    # NSFOCUS Corporation. All rights Reserved  
    ##################################################
  • 注释:由 # 开头的:适当注释原则,只注释原理和难点;多行注释:函数体的第一行使用三个双引号"""来定义
    为每一个包、模块、类、函数(方法)写 docstrings,对于函数要说明参数args、返回值returns,可能存在的异常raises
    可以通过对象的doc成员被自动提取, 并且被pydoc所用

  • 空格:不要在逗号, 分号, 冒号前面加空格, 但应该在它们后面加(除了在行尾)

  • 空行:在 import 不同种类的模块间加空行;
    在函数中的逻辑段落间加空行,即把相关的代码紧凑写在一起,作为一个逻辑段落,段落间以空行分隔;

  • 长度:最大长度不得超过 80 个字符的标准,超过了请强行换行

  • 命名:常量:全部大写;避免使用全局变量,用类变量来代替;

本文来自凡蜕博客(https://blog.ysboke.cn), 转载请带上地址.。
匿名

发表评论

匿名网友