Python个人笔记

目录

前言

本博客仅仅记录我学习使用Python期间遇到的一些问题,一些解决方案之类的,学习部分参考的廖雪峰的Python教程,学完之后做一个记录备忘

垃圾pip的下载速度

众多周知的原因,pip下载包的时候超级慢,慢到令人发指,所以使用国内的镜像会快很多,例如我要下载pillow,可以使用

pip install -i https://pypi.doubanio.com/simple/ pillow

在pip install -i https://pypi.doubanio.com/simple/ 后面加上要下载的包名就可以了,不要使用pip install pillow,慢的很

查看Python以及第三方包安装位置

查看Python的位置很简单,直接输入

where python

查看你现在安装的所有的包

pip list

一些第三方Python包一般都是通过pip下载的,查看这些包的位置以及其他信息可以使用

# 例子 pip show 包名
pip show googletrans 

Python打包成exe

安装pyinstaller打包工具

pip install pyinstaller

打包

需要进入到你的Python文件所在的地方,执行,顺便再提一下,这个图标啊,好像必须是ico格式的,我用gif不行,再有,打包之后的exe图标还是没变化,你把exe移出来换个地方图标就正常显示了

#如果有图标的话可以执行
pyinstaller -F -i favicon.ico nhdz.py
#如果没有图标,可以不写
pyinstaller -F nhdz.py

打包报错

错误 :AttributeError: module 'win32ctypes.pywin32.win32api' has no attribute 'error'
原因是因为我的图片不是 .ico格式的,只要不是.ico格式的图片好像不能作为图标,然后我把jpg改成ico,事实证明只改格式是不行的,所以,如果没有ico的图,就不要图标了吧

selenium打包之后给别人使用需要安装驱动

事情是这样的,有一个网页需要每天自动签到,然后我就写了个自动签到的工具,就是Python调用Selenium,打包成exe给也需要签到的我的朋友
但是他一打开就闪退,我给他打了个log发现别人电脑上没有python环境,想要运行selenium驱动chrome,必须安装ChromeDriver
ChromeDriver的exe版下载地址,下载完之后把这个exe放到一个文件夹,可以是Chrome的安装文件夹,也可以是任意一个文件夹,不过我喜欢和Chrome放在一起,好找,然后这个exe是不能双击安装的,你需要做的就是打开环境变量,把这个ChromeDriver所在的目录加到Path环境变量里面,然后再次运行我写的签到exe就ok了
如果还是出现了Chrome闪退,那就是Chrome版本和ChromeDriver版本不一致,好好对一下

Python操作json

储存json

想把数据变成json还是很简单的,首先键值对,我使用的是dict字典,然后json化

Specifications = {}

        for tr in table.find_elements_by_css_selector('tr'):
            if tr.get_attribute('class'):
               Specifications[tr.find_element_by_css_selector(
                    '.spec-name').text] = tr.find_element_by_css_selector('.spec-value').text

Specifications = json.dumps(Specifications)

Dict使用json.dumps就变成json字符串了,可以直接存储到数据库里面或者其他里面

读取json

从数据库读取json字符串

例如,我的json是这样的

{"ERRORCODE":"0","RESULT":[{"port":"45021","ip":"183.15.122.171"}]}

我可以这样读取

jsonobj = json.loads(str(r.text))
print(jsonobj)
print(jsonobj['RESULT'])
print(jsonobj['RESULT'][0]['ip'])
print(jsonobj['RESULT'][0]['port'])

如果多层就使用['XX']['XX'] 如果json里面带有[] 那就使用[0]

从配置文件读取json

我需要一个配置文件,使用json是很方便的,所以我按照上面读取数据库json字符串的方式读取本地json文件,报错了,正确做法如下

with open(r"config.json",'r',encoding='utf-8') as f:
    config = json.load(f)
    print(config)
    print(config['ConnectionString'])
    print(config['ProductCategoryID'])

注意,使用的是load方法,不是loads,如果这里使用loads方法,会报错

中文问题

如果有中文json化的话,肯能会出现下图的问题

可以json化的时候这样写

ParameterDetails=json.dumps(ParameterDetails,ensure_ascii=False, indent=4)

这样有中文的json化之后也可以看中文

倒计时关闭程序

我写爬虫时,遇到网站无法访问的问题,就卡在那,动也不动,写try catch退出也不行,卡那了,没进try catch,所以我写了一个计时功能,10分钟后退出exe,Windows的计划任务定时启动exe

# 10秒钟退出exe
import sys
import time

time_begin=int(time.time())
if(int(time.time()) - time_begin) >=10: #这里的单位是秒
       sys.exit()

Python递归之return None

代码如下

def judge_description( next):
    if next > 0 :
        if next > 3 :
            return next
        else:
            next = next + 1
            judge_description(next)
    else:
        return 0
    
print(judge_description(1))    

返回结果应该是4才对,但是返回的却是None,我居然没有发现else那里没有写return,正确的写法应该是

def judge_description( next):
    if next > 0 :
        if next > 3 :
            return next
        else:
            next = next + 1
            return judge_description(next)
    else:
        return 0
    
print(judge_description(1))    

多个not in的写法

我有多个条件需要判断,每一个都要满足不存在,我可以这样写

if a not in text and b not in text and....

这样写不好,五六个就已经很头疼了,可以使用all函数,如下

s=['6','8']
text='12345'
if all(t not in text for t in s):
    print('ok')

清除字符串前后的空格

仅仅清除前后的空格,字符串内部的空格不会被清除

    asd = ' asd asd  '
    asd = asd.strip()

asd的结果是asd asd

截取字符串

我想截取字符串如下

http://netdna-cdn.com/wp-content/uploads/2015/12/C4468-image.gif

我只想要uploads/2015/12/C4468-image.gif 这个部分,我原本想的是使用正则,可是大佬告诉我有更快的方法,就是使用截取

text = "http://netdna-cdn.com/wp-content/uploads/2015/12/C4468-image.gif"
print(text.index('uploads'))

sss=text[text.index('uploads'):]
aaa=sss.split('/')
print(aaa[0]+"--"+aaa[1]+aaa[2]+"---"+aaa[3])

讲解一下,首先text.index('uploads')是得到uploads的位置,然后根据这个位置我可以截取,通过[:]方式

text[text.index('uploads'):]就是从uploads开始截取到最后

然后就是split分割了,这个很简单

计算长度或者数量len()

只要是计算长度的,都可以使用len函数,计算数量的也可以使用len()函数,比如我查询了所有的a标签,我想知道a标签到底有多少个,我可以使用

pagelist = pages.find_elements_by_css_selector('a')
print(len(pagelist))

Python操作MongoDB

配置连接MongoDB

先安装PyMongo,然后代码很简单,只需要写几行代码就可以了

client = pymongo.MongoClient("mongodb://admin:test123@192.168.1.1:27017/")
db = client.database
collection = db.test

连接字符串里面是我的MongoDB的账号和密码,后面才是MongoDB的ip地址

插入数据

先定义一个集合,然后insert

message = {
    'PartNumber': a.text,
    'Address': a.get_attribute('href'),
    'url': url
}
collection.insert_one(message)

查询数据

这里我分为两种情况,一种是查询一堆,也就是模糊查询

def get_address():
    arr=[]
    datalist = collectionAddress.find({'url': re.compile('char=0&popular=1')})
    for data in datalist:
        arr.append(data['Address'])
    return arr    

还有一个情况,就是我插入数据的时候,我想检测数据是否已经存在MongoDB数据库了,如果存在就不插入

arrs = get_address()
for url in arrs:
    isexit = collectionData.find_one({'Address': url})
    if isexit:
        print('有数据')
    else:
        save_data(url)

这里要使用find_one,我使用find什么也查不出来

VS Code发布Python为exe

我之前爬虫都是直接F5运行,挂着VS Code爬取的,但是目前遇到了一个数据量很大而且很难爬的网站,决定多开几个爬虫的,同时爬取

先安装Python发布的库

pip install pyinstaller

打包exe的命令需要进入到你的python文件的目录,然后执行

pyinstaller -F data1.py

稍等片刻,就会生成exe

Python操作SqlServer

推荐查看官网的wiki文档:https://github.com/mkleehammer/pyodbc/wiki

导入库import pyodbc 这个库厉害了,不仅仅是SQLServer,Mysql,Oracle都是可以的,而且很好用

普通的写法

import pymongo
import re
import pyodbc


def insertsql(name,sex,age):#插入SQL数据库

    conn = pyodbc.connect('DRIVER={SQL Server};SERVER=127.0.0.1,1433;DATABASE=test;UID=sa;PWD=test123')  
    try:
        cursor = conn.cursor()
        # cursor.execute('insert into Test(Name,Sex,Age) values(?,?,?)',(name,sex,age))  插入
        # cursor.execute('delete from Test where Name=?',(name))  删除
        cursor.execute('select * from Test where Name = ?',(name)) #查询
        cursor.execute('update Test set Name=? where Name=?',('蜀云泉','许嵩'))  #更新

        # data=cursor.fetchone()  查询一个
        data=cursor.fetchall()

        print(data)

        # conn.commit()# 插入和删除,更新数据的时候执行,查询不需要执行
        print('成功')
    except Exception as e:
        print(str(e))
    finally:
        conn.close()


insertsql('许嵩','男',35)

超级爽的写法

pymongo用起来非常的舒服,就是下面的写法

cursor.execute("""
    select * from Test where Name like '%许嵩%'
    """
    )

如果想加参数传入可以这样

cursor.execute("""
    select * from Test where Name like ?
    """,'%许嵩%'
    )

查询结果

你可能发现了,查询的结果是下面这样的

[(2, '许嵩', '男 ', 32), (3, '许嵩', '女 ', 33), (4, '许嵩', '男 ', 30), (6, '许嵩 ', '男 ', 35)]

有中括号,还有括号,其实可以这样得到想要的数据

cursor.execute("""
    select Name,Sex,Age from Test where Name like '%许嵩%'
    """
    )

datalist=cursor.fetchall()
for data in datalist:
    print(data.Name)
    print(data.Sex)
    print(data.Age)

指名想要的列,然后就可以遍历输出了,结果如下

许嵩

32
许嵩

33
许嵩

30
许嵩

35

pyodbc操作SQLserver,获取刚插入数据的Id

这个是真的麻烦,我在数据库里面尝试这个语句是完全ok的

select ident_current('表名')

但是在python里面写了获取的时候根本获取的不是这样的,获取的是包含Decimal的一个什么类型

没办法,我无法解决,百度,Google了半天,官网也看了,还是没解决,我真是🐷

没办法,我最后只好使用一个字符串截取的方法了

cursor.execute("""
    select CAST((select ident_current('VaeDB.dbo.Netcomponents_SupplierInformation')) as Integer)
""")

SupplierInformationId = str(cursor.fetchone())
SupplierInformationId=int(SupplierInformationId[SupplierInformationId.index('(')+1:SupplierInformationId.index(',')])

Python操作Mysql

操作SQL Server的 pyodbc 就可以直接操作Mysql,但是我是头🐷

我看不懂官方文档是怎么操作的,网上的博客抄来抄去没有实质内容,所以换了一个包

先安装操作MySQL的包

pip install pymysql

然后连接字符串改变了,其他的都没变,所以只放出连接字符串

import pymysql

conn = pymysql.connect(
    host='192.168.1.1', user='root', passwd='123456',
    port=3306, db='VaeDB', charset='utf8'
)

cursor = conn.cursor()

pymysql操作

pymysql操作mysql和pyodbc操作Sql Server一点都不一样

# 查询
sql="select id from Memory where MemoryName='%s'" %MemoryName
mysqlcursor.execute(sql)
id=mysqlcursor.fetchone()[0]

# 增加一条
cursor.execute("""
    INSERT product (`Name`) VALUES('yh')
    """)
conn.commit()
id=cursor.lastrowid
print('Id是:' + str(id))
    
# 增加多条
datalist=[]
if Density != '':
    tuple=(id,'Density',Density)
    datalist.append(tuple)
if Org != '':
    tuple=(id,'Org',Org)
    datalist.append(tuple)
if Refresh != '':
    tuple=(id,'Refresh',Refresh)
    datalist.append(tuple)

sqls="INSERT INTO `MemoryParameter`( `MemoryId`, `MemoryParameterName`, `MemoryParameterValue`) VALUES (%s,%s,%s);"
mysqlcursor.executemany(sqls,datalist)
mysqlconn.commit()

插入之后立即获取插入后的ID

注意:commit执行是conn的事,我遇到了插入之后就需要Id的问题,很简单,执行一个cursor.lastrowid就可以

if __name__ == "__main__":
    cursor.execute("""
    INSERT product (`Name`) VALUES('yh')
    """)
    conn.commit()
    id=cursor.lastrowid
    print('Id是:' + str(id))

基础

List:数组

#append直接在后面加上
list.append('林俊杰')
#insert可以跟数字,制定插入的位置
list.insert(1,'张泉')
#删除最后一个元素
list.pop()
#删除指定的数据,删除第二个元素
list.pop(1)
#改
list[1]='唐宋元明清'
#可以print出来查看
print str(list).decode('string_escape')
#可以查看list的长度
print len(list)
#获取倒数的内容
print list[-1]  #倒数第一
print list[-2]  #倒数第二

Tuple:不可变的数组

Tuple和List差不多,但是是(),而且Tuple不可变,但是Tuple内部的List可以改,类似下面的tuple里面的第三个元素是List就可以变,前两个tuple元素不可变

tuple=(1,True,['许嵩','蜀云泉'])

range():生成整数

range(5)就是[0, 1, 2, 3, 4]
range(100)就是[0, 1, 2, 3, 4 ... 99]

循环

可以使用for循环也可以使用while循环

  • break:跳出循环,结束
  • continue:跳过本次循环,进入下一次循环

dict:key-value形式的字典

d = {'Michael': 95, 'Bob': 75, 'Tracy': 85}
#读取
d['Michael']
#赋值
d['Adam'] = 67

set:不会重复的集合

s = set([1, 2, 3])
s.add(4)
s.remove(4)

常用函数

abs():绝对值

abs(-100)

max():返回最大值

#结果返回3
max(2, 3, 1, -5)

随机数

num = random.randint(2,5) 随机2~5,包括2和5

类型转换

int('123')
float('12.34')
str(100)
bool(1)

自定义函数

def nop():
    pass #什么也不做就写pass

返回多个值,本质就是返回一个tuple,tuple的()是可以省略的

import math
def move(x, y, step, angle=0):
    nx = x + step * math.cos(angle)
    ny = y - step * math.sin(angle)
    return nx, ny

# 调用
 x, y = move(100, 100, 60, math.pi / 6)

可变参数: *参数

普通的参数就不说了,例如

def enroll(name, gender, age=6, city='Beijing'):
def add_end(L=[]):    

可变参数可以像这样写,numbers前面加了一个*,那么接受的就是一个tuple了

def calc(*numbers):
    sum = 0
    for n in numbers:
        sum = sum + n * n
    return sum

递归函数

这个也很常用

def fact(n):
    if n==1:
        return 1
    return n * fact(n - 1)

高级特性

切片:快速获取数组前几个内容

有一个数组如下

arr=['1','2','3','4','5']

我想取数组的前3个元素,可以使用arr[0],arr[1],arr[2]来获取,那如果我想获取前20个元素我可以写一个for循环取到.但是,切片最简单

#索引从0开始,取到2,是不包括后边的索引3的
arr[0:3]
#倒着取数据
arr[-2:]

列表生成式:for循环的快速计算

#我想生成[1x1, 2x2, 3x3, ..., 10x10]
L = []
for x in range(1, 11):
	L.append(x * x)
    
#上面的循环可以实现,但是不好,使用下面的列表生成式
[x * x for x in range(1, 11)]
[x * x for x in range(1, 11) if x % 2 == 0]
[m + n for m in 'ABC' for n in 'XYZ']

生成器:列表生成式的[]改为()就是生成器

生成器和列表生成式的区别就是生成器可以for循环

g = (x * x for x in range(10))
for n in g:
   print(n)

函数式编程

高阶函数:函数的参数是函数

def add(x, y, f):
    return f(x) + f(y)
#调用例子
add(-5, 6, abs)

map():将函数作用于Iterable的每个元素

def f(x):
	return x * x

r = map(f, [1, 2, 3, 4, 5, 6, 7, 8, 9])
#r的结果
[1, 4, 9, 16, 25, 36, 49, 64, 81]

#list全部元素转换为字符串
list(map(str, [1, 2, 3, 4, 5, 6, 7, 8, 9]))
#结果是
['1', '2', '3', '4', '5', '6', '7', '8', '9']

reducr():必须接受两个参数的函数作用于序列的下一个元素

#把序列[1, 3, 5, 7, 9]变换成整数13579
from functools import reduce
def fn(x, y):
   return x * 10 + y

reduce(fn, [1, 3, 5, 7, 9])
13579

filter():和map类似,函数返回的是bool类型,主要功能过滤筛选

#函数返回true保留,返回false丢弃
def is_odd(n):
    return n % 2 == 1

list(filter(is_odd, [1, 2, 4, 5, 6, 9, 10, 15]))
# 结果: [1, 5, 9, 15]

#把一个序列中的空字符串删掉,可以这么写:
def not_empty(s):
    return s and s.strip()

list(filter(not_empty, ['A', '', 'B', None, 'C', '  ']))
# 结果: ['A', 'B', 'C']

sort():排序

sorted([36, 5, -12, 9, -21])
#结果
[-21, -12, 5, 9, 36]

#传入key参数,忽略大小写的排序
sorted(['bob', 'about', 'Zoo', 'Credit'], key=str.lower)
['about', 'bob', 'Credit', 'Zoo']

面向对象编程

Python里的类

init就相当于构造函数,Python类里面的所有的方法的第一个参数永远是self,表示创建的实例本身

讲一下%s这个,%s的意思是后面的变量格式化为一个string的字符串,并且有占位符的作用,所以我写了两个%s 后面%后接着的就是变量,多个变量可以括起来

class Student(object):
    def __init__(self,name,score):
        self.name=name
        self.score=score

    def print_score(self):
        print('%s: %s' % (self.name, self.score))


Vae=Student('许嵩',100)
JJ=Student('林俊杰',100)
Vae.print_score()
JJ.print_score()

上面的类可以随意的给属性name和score赋值,如果想把属性变成私有的,可以使用两个下划线,这样就变成私有的了,如下

class Student(object):
    def __init__(self, name, score):
        self.__name = name
        self.__score = score

    def print_score(self):
        print('%s: %s' % (self.__name, self.__score))

这样一来,你就无法访问name和score属性了,无法读取和赋值了

但是还是可以读取和赋值私有属性,如下

Vae._Student__name='蜀云泉'
print(Vae._Student__name)

所以,Python的私有全靠自学,Python本身是没有限制的,靠你的自学去准守规则

继承和多态

上面的Python类里面,我写了一个参数是object,这个参数是继承使用的,如

class Animal(object):
    def run(self):
        print('Animal is running...')
 
class Dog(Animal):
    pass

dog = Dog()
dog.run()

获取对象信息:type()获取类型,dir()获取属性方法

可以看对象的类型和方法,感觉有点反射的意思啊

print(type(1))
print(type("as"))
print(type(Vae))
#结果如下
<class 'int'>
<class 'str'>
<class '__main__.Student'>

isinstance()方法可以判断类的依赖关系,前面是子类的实例对象,后面是基类名称

print(isinstance(dog, Animal))

接下来是获取所有属性和方法的dir方法

print(dir(Vae))
#结果
['_Student__name', '_Student__score', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__',
'__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'print_score']

hasattr()可以判断属性存不存在,但貌似只能判断public的

print(hasattr(Vae,'name'))
#可以传入一个default参数,如果属性不存在,就返回默认值:
print(getattr(obj, 'z', 404))

类属性和实例属性

这个有趣啊,实例找不到属性的时候会去类里面找,其他语言里面,类.属性都是得静态类才可以的,Python直接可以

class Student(object):
    name = 'Student'
    
s = Student() # 创建实例s
print(s.name)
print(Student.name) # 打印类的name属性

面向对象高级

__slots__限制动态添加属性

在Python中,使用Vae.age=18就可以为Vae对象添加一个age属性,如果想限制一个类的实例只能动态的添加指定的属性,可以使用__slots__

class Student(object):
    __slots__ = ('name','age')

Vae=Student()
Vae.name='许嵩'
Vae.age=33
print(Vae.name)
print(Vae.age)

我不明白,这个限制属性的__slots__后面不可以写方法了,我一个类只写一个限制属性的玩意,其他内容都写不了???

@property:get,set方法变成属性调用

我们在写类的时候,属性一般都写get和set方法,这样可以对数据做一个判断,例如

class Student(object):
    
    def get_score(self):
         return self._score

    def set_score(self, value):
        if not isinstance(value, int):
            raise ValueError('score must be an integer!')
        if value < 0 or value > 100:
            raise ValueError('score must between 0 ~ 100!')
        self._score = value


Vae=Student()
Vae.set_score(100)
print(Vae.get_score())

调用的时候都是方法set_score(),get_score()

然后可以变成属性.....真无聊啊这个东西.....

class Student(object):
    
    @property
    def score(self):
        return self._score

    @score.setter
    def score(self, value):
        if not isinstance(value, int):
            raise ValueError('score must be an integer!')
        if value < 0 or value > 100:
            raise ValueError('score must between 0 ~ 100!')
        self._score = value


Vae=Student()
Vae.score=100
print(Vae.score)

可以看到,方法名称都变成score了,调用的时候也是score,代码少了很多吗?

多重继承

就是一个子类继承两个父类,多个父类,除了第一个后面的需要加MixIn

class Dog(Mammal, RunnableMixIn, CarnivorousMixIn):
    pass

枚举

from enum import Enum
Month = Enum('Month', ('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'))

IO

读取文件

原始写法,手动close

文件的读取操作之后,需要关闭文件,不然就会占用系统资源,所以原始写法是这样的

try:
    f = open('/path/to/file', 'r',,encoding='utf-8')
    print(f.read())
finally:
    if f:
        f.close()

很麻烦,所以Python提供了一个方便的写法

方便写法,open方法

import sys
path = 'D:\Git\StudyPython\StudyCode\data\studyIO.txt'

with open(path,'r',encoding='utf-8') as f: 
    print(f.read())

相对路径

    with open(r"userinfo.txt",'r',encoding='utf-8') as f:
        print(f.readline)

这个和原始写法的try catch是一样的,自动close了

读取方式

Python读取文件的方式有三种,看情况使用

  • read():一次性读取文件全部内容,如果文件很大,比如10G,内存可能会崩,所以read方法适合小文件
  • read(size)方法,每次读取固定大小,这种方式最普遍适用
  • readline(),每次读取一行
  • readlines(),一次性读取全部内容,以list的形式返回.如果文件过大会爆内存

代码如下

import sys

path = 'D:\Git\StudyPython\StudyCode\data\studyIO.txt'

# read方法,一次性读取全部内容,如果文件过大会爆内存
with open(path,'r',encoding='utf-8') as f:
    print(f.read())

# read(size)方法,每次读取固定大小
with open(path,'r',encoding='utf-8') as f:
    while True:
        x = f.read(10)
        if not x:
            break
        print(x)

# readline方法,每次读取一行
with open(path,'r',encoding='utf-8') as f:
    for line in f.readline():
        print(line)

# readlines方法,一次性读取全部内容,以list的形式返回.如果文件过大会爆内存
with open(path,'r',encoding='utf-8') as f:
    for line in f.readlines():
        print(line)        

如果文件很小,read()一次性读取最方便;如果不能确定文件大小,反复调用read(size)比较保险;如果是配置文件,调用readlines()最方便

二进制文件读取

前面的读取read方法都是文件格式的,也可以读取成二进制文件

import sys
path = 'D:\Git\StudyPython\StudyCode\data\studyIO.txt'

# read方法,一次性读取全部内容,如果文件过大会爆内存
with open(path,'rb') as f:
    print(f.read())

忽略非编码字符

如果读取的文件有非编码字符的话,可以errors忽略

with open(path,'r', encoding='utf-8', errors='ignore') as f:

写文件

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

import sys
path = 'D:\Git\StudyPython\StudyCode\data\studyIO.txt'

with open(path,'r',encoding='utf-8') as f: #相对路径    path=r"userinfo.txt"
    f.write('Hello 许嵩 最佳歌手')

如果文件已存在,会直接覆盖.如果我们希望追加到文件末尾可以传入'a'

import sys
path = 'D:\Git\StudyPython\StudyCode\data\studyIO.txt'

with open(path,'a',encoding='utf-8') as f: #相对路径    path=r"userinfo.txt"
    f.write('蜀云泉很帅,没意见吧' + '\n')  #可以加一个 \n来换行

StringIO:内存中读写str文件

写入内存

write就是写入内存,读取可以使用getvalue()

from io import StringIO

f=StringIO()
f.write('许嵩,最佳歌手')   
f.write('蜀云泉')     
print(f.getvalue())

读取内存的文件

除了上面的getvalue()还有其他的方法

from io import StringIO

f=StringIO('我是已有的字符串\n我是第二行')
while True:
    s=f.readline()
    if s == '':
        break
    print(s.strip())

所以读取内存文件的方式有两种,一种全部读取getvalue(),一种是一行一行的读取readline()

BytesIO:内存中读写二进制文件

写二进制

from io import BytesIO

f=BytesIO()
f.write('中文'.encode('utf-8'))
print(f.getvalue())

读二进制

f=BytesIO(b'\xe4\xb8\xad\xe6\x96\x87')
print(f.read())

操作文件和目录

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

同样的道理,要拆分路径时,也不要直接去拆字符串,而要通过os.path.split()函数,这样可以把一个路径拆分为两部分,后一部分总是最后级别的目录或文件名

# 查看当前路径
print(os.path.abspath('.'))
# 在当前路径下直接新建文件夹
os.mkdir('Vae')
# 在当前路径下直接删除文件夹
os.rmdir('Vae')
# 指定的文件夹下面新建文件夹
os.mkdir('StudyCode/Vae')
# 指定的文件夹下删除文件夹
os.rmdir('StudyCode/Vae')
直接获取到文件的后缀扩展名
print(os.path.splitext('/StudyCode/data/studyIO.txt'))
# 文件重命名
os.rename('test.txt', 'test.py')
# 删除文件
os.remove('test.py')

序列化

Python的序列化的意思居然是内存的变量保存到本地磁盘的意思....

在其他语言里面这就是持久化.........

进程和线程

进程

multiprocessing

from multiprocessing import Process
import os

# 子进程要执行的代码
def run_proc(name):
    for i in range(0,200):
        print('我是子进程: '+str(i)+'\n')

if __name__=='__main__':
    p = Process(target=run_proc, args=('test',))
    p.start()
    # p.join() 如果想子线程执行完成再执行主线程,可以使用join方法,通常用于进程的同步
    for i in range(0,200):
        print('我是父进程: '+str(i)+'\n')

结果是父进程和子进程交错输出的

Pool进程池,多个进程的启动

from multiprocessing import Pool
import os, time, random

# 子进程要执行的代码
def run_proc(name):
    for i in range(0,200):
        print(name +str(i)+'\n')

if __name__ == '__main__':
    try:
        p = Pool(3)
        p.apply_async(run_proc, args=('1进程: ',))
        p.apply_async(run_proc, args=('2进程: ',))
        p.apply_async(run_proc, args=('3进程: ',))
        p.close()
        # p.join() 进程的同步,执行完3个子进程才会执行主进程,join必须在close之后

        for i in range(0,200):
            print('主进程' +str(i)+'\n')

    except Exception as identifier:
        pass       

进程之间的通信

进程间通信是通过QueuePipes等实现

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__':
    # 父进程创建Queue,并传给各个子进程:
    q = Queue()
    pw = Process(target=write, args=(q,))
    pr = Process(target=read, args=(q,))
    # 启动子进程pw,写入:
    pw.start()
    # 启动子进程pr,读取:
    pr.start()
    # 等待pw结束:
    pw.join()
    # pr进程里是死循环,无法等待其结束,只能强行终止:
    pr.terminate()

多线程

Python的标准库提供了两个模块:_threadthreading_thread是低级模块,threading是高级模块,对_thread进行了封装。绝大多数情况下,我们只需要使用threading这个高级模块。

import time, threading

# 新线程执行的代码:
def loop():
    n = 0
    print('线程开启: '+threading.current_thread().name)
    while n < 5:
        n = n + 1
        print('线程计数: '+str(n))
        time.sleep(1)
    print('线程结束: '+threading.current_thread().name)


if __name__ == "__main__":
    print('当前线程开始:'+threading.current_thread().name)
    t = threading.Thread(target=loop, name='Vae')
    t.start()
    t.join()
    print('当前线程结束:'+threading.current_thread().name)

执行结果是

当前线程开始:MainThread 线程开启: Vae 线程计数: 1 线程计数: 2 线程计数: 3 线程计数: 4 线程计数: 5 线程结束: Vae 当前线程结束:MainThread

主方法里面默认就 有一个线程,名字默认就是MainThread

我们使用threading.Thread新开了一个线程,执行loop方法,并且指定了线程名是Vae

Lock:锁

多进程之间变量是每个进程都有一份,而多线程是共享变量的,所以会出现多个线程修改同一个变量从而导致变量数据出错的情况,如下

import time, threading

# 假定这是你的银行存款:
balance = 0

def change_it(n):
    # 先存后取,结果应该为0:
    global balance
    balance = balance + n
    balance = balance - n

def run_thread(n):
    for i in range(100000):
        change_it(n)

t1 = threading.Thread(target=run_thread, args=(5,))
t2 = threading.Thread(target=run_thread, args=(8,))
t1.start()
t2.start()
t1.join()
t2.join()
print(balance)    

上面的代码多运行几次,会发现,有时候结果是0有时候却不是0,所以当一个线程在修改一个变量的时候,其他线程不允许访问,这个时候就需要加锁

# 假定这是你的银行存款:
balance = 0
lock=threading.Lock()

def change_it(n):
    # 先存后取,结果应该为0:
    global balance
    balance = balance + n
    balance = balance - n

def run_thread(n):
    for i in range(100000):
        #加锁
        lock.acquire()
        try:
            change_it(n)
        finally:
            #改完释放锁
            lock.release()
        

t1 = threading.Thread(target=run_thread, args=(5,))
t2 = threading.Thread(target=run_thread, args=(8,))
t1.start()
t2.start()
t1.join()
t2.join()
print(balance)    

使用lock = threading.Lock()获取了锁对象,在修改方法之前调用 lock.acquire()锁住了变量,执行完之后又释放了锁

Python的GIL全局锁

因为Python的线程虽然是真正的线程,但解释器执行代码时,有一个GIL锁:Global Interpreter Lock,任何Python线程执行前,必须先获得GIL锁,然后,每执行100条字节码,解释器就自动释放GIL锁,让别的线程有机会执行。这个GIL全局锁实际上把所有线程的执行代码都给上了锁,所以,多线程在Python中只能交替执行,即使100个线程跑在100核CPU上,也只能用到1个核。

GIL是Python解释器设计的历史遗留问题,通常我们用的解释器是官方实现的CPython,要真正利用多核,除非重写一个不带GIL的解释器。

所以,在Python中,可以使用多线程,但不要指望能有效利用多核。如果一定要通过多线程利用多核,那只能通过C扩展来实现,不过这样就失去了Python简单易用的特点。

不过,也不用过于担心,Python虽然不能利用多线程实现多核任务,但可以通过多进程实现多核任务。多个Python进程有各自独立的GIL锁,互不影响。

Python解释器由于设计时有GIL全局锁,导致了多线程无法利用多核。多线程的并发在Python中就是一个美丽的梦。

ThreadLocal:线程的局部变量

多个线程之间,使用全局变量还得加锁,使用局部变量,线程的每个方法调用的时候又麻烦,所以可以使用ThreadLocal

import threading
    
# 创建全局ThreadLocal对象:
local_school = threading.local()

def process_student():
    # 获取当前线程关联的student:
    std = local_school.student
    print('Hello, %s (in %s)' % (std, threading.current_thread().name))

def process_thread(name):
    # 绑定ThreadLocal的student:
    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()

一个ThreadLocal变量虽然是全局变量,但每个线程都只能读写自己线程的独立副本,互不干扰。ThreadLocal解决了参数在一个线程中各个函数之间互相传递的问题

ThreadLocal最常用的地方就是为每个线程绑定一个数据库连接,HTTP请求,用户身份信息等,这样一个线程的所有调用到的处理函数都可以非常方便地访问这些资源。

分布式进程

主进程,输出结果

#!/usr/bin/env python3
# -*- coding : utf-8 -*-
# master.py for windows
import time,queue
from multiprocessing.managers import BaseManager
from multiprocessing import freeze_support
#任务个数
task_number = 10;
#定义收发队列
task_queue = queue.Queue(task_number);
result_queue = queue.Queue(task_number);
def gettask():
    return task_queue;
def getresult():
    return result_queue;
def test():
    #windows下绑定调用接口不能使用lambda,所以只能先定义函数再绑定
    BaseManager.register('get_task',callable = gettask);
    BaseManager.register('get_result',callable = getresult);
    #绑定端口并设置验证码,windows下需要填写ip地址,linux下不填默认为本地
    manager = BaseManager(address = ('127.0.0.1',5002),authkey = b'123');
    #启动
    manager.start();
    try:
        #通过网络获取任务队列和结果队列
        task = manager.get_task();
        result = manager.get_result();
        #添加任务
        for i in range(task_number):
            print('Put task %d...' % i)
            task.put(i);
        #每秒检测一次是否所有任务都被执行完
        while not result.full():
            time.sleep(1);
        for i in range(result.qsize()):
            ans = result.get();
            print('task %d is finish , runtime:%d s' % ans);
    
    except:
        print('Manager error');
    finally:
        #一定要关闭,否则会爆管道未关闭的错误
        manager.shutdown();
        
if __name__ == '__main__':
    # windows下多进程可能会炸,添加这句可以缓解
    freeze_support()
    test();

新开进程,执行任务写入结果

#!/usr/bin/env python3
# -*- coding : utf-8 -*-
# task.py for windows
import time
import sys
import queue
import random
from multiprocessing.managers import BaseManager

BaseManager.register('get_task')
BaseManager.register('get_result')
conn = BaseManager(address=('127.0.0.1', 5002), authkey=b'123')
try:
    conn.connect()
except:
    print('连接失败')
    sys.exit()
task = conn.get_task()
result = conn.get_result()
while not task.empty():
    n = task.get(timeout=1)
    print('run task %d' % n)
    sleeptime = random.randint(0, 3)
    time.sleep(sleeptime)
    rt = (n, sleeptime)
    result.put(rt)
if __name__ == '__main__':
    pass

结果展示

内建模块

datetime

import time
from datetime import datetime,timedelta

vae = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())

# 获取当前时间
print(vae) #结果 2019-08-20 23:19:48
print(datetime.now()) # 结果 2019-08-20 23:19:48.417210
# datetime转化为timestamp (就是时间戳)
print(datetime.now().timestamp()) # 结果 1566314388.418206
# timestamp转化为datetime
t=1429417200.0
print(datetime.fromtimestamp(t)) # 结果 2015-04-19 12:20:00
# str转化为datetime,这个常用,字符串格式的时间转为时间格式
cday = datetime.strptime('2015-6-1 18:19:59', '%Y-%m-%d %H:%M:%S')
print(cday) # 结果 2015-06-01 18:19:59
# datetime转化为str字符串
now = datetime.now()
print(now.strftime('%a, %b %d %H:%M')) # 结果 Tue, Aug 20 23:27
# datetime加减,这个也常用,需要导入timedelta类,用法如下
datetime.now() + timedelta(hours=10)
datetime.now() - timedelta(days=1)
datetime.now() + timedelta(days=2,hours=10)

Python访问返回json数据的接口

很简单,直接Request请求


import requests
import json


headers = {
    'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac 0S X 10_11_4) AppleWebKit/537.36 (KHTML, like Gecko)Chrome/52.0.2743.116 Safari/537.36'}
    
def get_country(url,id):
    try:
        headers = {
        'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac 0S X 10_11_4) AppleWebKit/537.36 (KHTML, like Gecko)Chrome/52.0.2743.116 Safari/537.36'}
    
        response = requests.request("GET", url, headers=headers)
        jsonobj = json.loads(response.text)
        print(jsonobj['data'][0]['country'])

    except Exception:
        pass

if __name__ == "__main__":

        get_country('https://www.v2ex.com/api/nodes/show.json')


posted @ 2019-07-07 19:57  蜀云泉  阅读(949)  评论(0编辑  收藏  举报