美文网首页
python 一些常用的小技巧

python 一些常用的小技巧

作者: Senter | 来源:发表于2019-03-13 10:40 被阅读0次

1,for 删除中的问题

list1 = [1,2,3,4,5,6]
for i in list1:
    list1.remove(i)
    print(i)
print(list1)

for 语句如何循环的呢?步骤是:
(1)先判断对象是否为可迭代对象,不是的话直接报错,抛出TypeError异常,是的话,调用 __iter__或iter方法,返回一个迭代器
(2)不断地调用迭代器的__next__或next方法,每次按序返回迭代器中的一个值
(3)迭代到最后,没有更多元素了,就抛出异常 StopIteration,这个异常 python 自己会处理,不会暴露给开发者


因为执行for语句时,先生成一个迭代器,再用next方法,按序返回迭代器中的值,
当列表中删除了元素2时,元素3的索引变成了原来元素2的索引,
把元素3往前推到了元素2的位置,所以next方法就没返回值3.

2.提高numpy运行速度

import time
def foo(x,y):
    tt = time.time()
    s = 0
    for i in range(x,y):
        s += i
    print('Time used: {} sec'.format(time.time()-tt))
  return s
print(foo(1,100000000))


# 修改后
from numba import jit
import time
@jit
def foo(x,y):
  tt = time.time()
  s = 0
  for i in range(x,y):
    s += i
  print('Time used: {} sec'.format(time.time()-tt))
  return s
print(foo(1,100000000))

numba中提供了一些修饰器,它们可以将其修饰的函数JIT编译成机器码函数,并返回一个可在Python中调用机器码的包装对象。为了能将Python函数编译成能高速执行的机器码,我们需要告诉JIT编译器函数的各个参数和返回值的类型。

JIT所产生的函数只能对指定的类型的参数进行运算
如果希望JIT能针对所有类型的参数进行运算,可以使用autojit:

Numba和Jupyter更适合适合GPU计算的实验
Numba可以同时为CPU和GPU编译
Numba + Jupyter =快速CUDA原型开发

3.文件的读取

# 方法一:

# glob.glob  匹配全部后缀为.csv的文件(生成为列表)

"""
glob: 返回所有匹配的文件路径列表(list);
      其返回的文件名只包括当前目录里的文件名,不包括子文件夹里的文件
"""

for i in glob.glob('./*.csv'):
    fp = pd.read_csv(i)
    # 写入成csv文件\
    fp.to_csv('city_ocean.csv',mode='a',index=False)

方法二:

os.walk(): 可以遍历文件夹下所有的文件

os.walk(top, topdown=Ture, onerror=None, followlinks=False)

该函数可以得到一个三元tupple(dirpath, dirnames, filenames).

  • dirpath:string,代表目录的路径;
  • dirnames:list,包含了当前dirpath路径下所有的子目录名字(不包含目录路径);
  • filenames:list,包含了当前dirpath路径下所有的非目录子文件的名字(不包含目录路径)。
import os  
  
def file_name(file_dir):   
    for root, dirs, files in os.walk(file_dir):  
        print(root)     #当前目录路径  
        print(dirs)     #当前路径下所有子目录  
        print(files)    #当前路径下所有非目录子文件 
        

# 当需要特定类型的文件时
def file_name(file_dir):   
    L=[]   
    for root, dirs, files in os.walk(file_dir):  
        for file in files:  
            if os.path.splitext(file)[1] == '.jpeg':  
                L.append(os.path.join(root, file))  
    return L  

python3实现链表

python是动态语言,可以直接把对象赋值给新的变量。在C/C++中,通常采用“指针+结构体”来实现链表;而在Python中,则可以采用“引用+类”来实现链表。

链表的定义:是一组数据项的集合,其中每个数据项都是一个节点的一部分,每个节点还包含指向下一个节点的链接

链表的结构:data为自定义的数据,next为下一个节点的地址。

基本元素:

  1. 节点:每个节点有两个部分,左边部分称为值域,用来存放用户数据;右边部分称为指针域,用来存放指向下一个元素的指针。
  2. head:head节点永远指向第一个节点
  3. tail: tail永远指向最后一个节点
  4. None:链表中最后一个节点的指针域为None值
# 链表结构实现  私有属性_pro_item是指向下个节点的指针,_item为此节点的值
class ChainDemo():
    def __init__(self,item = None,pos_item=None):

        self._item = item
        self._pos_item = pos_item


if __name__ == '__main__':
    chain = ChainDemo('A',(ChainDemo('B',ChainDemo('C',ChainDemo('D')))))
    while True:
        print(chain._item)
        if chain._pos_item != None:
            chain = chain._pos_item
        else:
            break

2、实现对链表的操作(增删)

#链表节点结构实现  私有属性_pro_item是指向下个节点的指针,_item为此节点的值
class Node():

    def __init__(self,item = None,pos_item=None):

        self._item = item
        self._next = pos_item

    def __repr__(self):
        '''
        用来定义Node的字符输出,
        print为输出item
        '''
        return str(self._item)

#单链表实现
class Chain():

    def __init__(self):
        self._head = None
        self.length = 0

    #判空
    def isEmpty(self):
        return self.length == 0

    #链表结尾插入
    def append(self,item):

        if isinstance(item,Node):
            node = item
        else:
            node = Node(item)


        if self._head == None:
            self._head = node
        else:
            be_node = self._head
            while be_node._next:
                be_node = be_node._next
            be_node._next = node
        self.length += 1


    #插入数据
    def insert(self,index,item):

        if self.isEmpty():
            print('this chain table is empty')
            return

        if index<0 or index >= self.length:
            print("error: out of index")
            return

        in_node = Node(item)
        node  = self._head
        count = 1

        while True:
            node = node._next
            count += 1
            if count == index:

                next_node = node._next
                node._next = in_node
                in_node._next = next_node
                self.length += 1
                return


            # node = s


    #删除数据
    def delete(self,index):

        if self.isEmpty():
            print('this chain table is empty')
            return

        if index<0 or index >= self.length:
            print("error: out of index")
            return
        # if index == 0
        #     self._head = None
        else:
            node = self._head
            count = 0
            while True:
                count += 1
                if index == count:
                    node._next = node._next._next
                    break
                node = node._next


        self.length -= 1

    def __repr__(self):
        if self.isEmpty():
            print("the chain table is empty")
            return
        nlist = ""
        node = self._head
        while node:
            nlist += node._item +''
            node = node._next
        return nlist


if __name__ == '__main__':
    chain = Chain()
    chain.append('A')
    chain.append('B')
    chain.append('C')
    chain.append('D')
    chain.append('E')
    chain.append('F')
    chain.append('G')
    chain.insert(4,'p')
    chain.delete(3)
    print(chain,chain._head._item,chain.length)

4.mysql操作(事务、游标)案例

1. mysql事务

MySQL事务主要用于处理操作量大,复杂度高的数据。
比如,你操作一个数据库,公司的一个员工离职了,你要在数据库中删除他的资料,也要删除该人员相关的,比如邮箱,个人资产等。这些数据库操作语言就构成了一个事务。

mysql事务的方法

commit():提交当前事务,如果是支持事务的数据库执行增删改后没有commit则数据库默认回滚,白操作了
rollback():取消当前事务

2.游标

游标(cursor)

游标是系统为用户开设的一个数据缓冲区,存放SQL语句的执行结果
用户可以用SQL语句逐一从游标中获取记录,并赋给主变量,交由python进一步处理,一组主变量一次只能存放一条记录

常用方法:

cursor():创建游标对象
close():关闭此游标对象

案例

两人相互转账,

1, 检查账户是否存在合法
2, 检查转账金额与余额适合合理
3, 检查账户加钱,减钱是否成功
4, 任何一步发生错误的时候就进行回滚

import pymysql

# 定义一个函数类
class TransferMoney(object):
    def __init__(self,conn):
        self.conn = conn

    # 检查账户是否合法text02.py:
    def check_acct_avaiable(self,acctid):
        # 游标
        cursor = self.conn.cursor()
        try:
            sql = "select * from account where acctid=%s" % acctid
            cursor.execute(sql)
            print ("检查账户:" + sql)
            rs = cursor.fetchall()
            # print(len(rs))
            if len(rs) != 1:
                raise Exception("账户 %s 是不合法的" % acctid)
        finally:
            cursor.close()

    # 检查是否有足够的钱
    def has_enough_money(self,acctid,money):
        cursor = self.conn.cursor()
        try:
            sql = "select * from account where acctid=%s and money > %s" % (acctid,money)
            cursor.execute(sql)

            print ("有足够的money:" + sql)
            rs = cursor.fetchall()
            if len(rs) != 1:
                raise Exception("账户 %s 没有足够的余额" % acctid)
        finally:
            cursor.close()


    # 账户减钱
    def reduce_money(self,acctid,money):
        cursor = self.conn.cursor()

        try:
            sql = "update account set money = money-%s where acctid = %s" % (money,acctid)
            cursor.execute(sql)
            print ("减钱:" + sql)
            if cursor.rowcount != 1:
                raise Exception("减钱失败 %s" % acctid)
        finally:
            cursor.close()

    # 账户加钱
    def add_money(self,acctid,money):
        cursor = self.conn.cursor()
        try:
            sql = "update account set money = money+%s where acctid = %s" % (money,acctid)
            cursor.execute(sql)
            print ("加钱:" + sql)
            if cursor.rowcount != 1:
                raise Exception("加钱失败 %s" % acctid)
        finally:
            cursor.close()


    # 主执行语句
    def transfer(self,source_acctid,target_acctid,money):
        try:
            # 检查给钱的账户
            self.check_acct_avaiable(source_acctid)
            # 检查收钱的账户
            self.check_acct_avaiable(target_acctid)
            # 检查是否有足够的钱
            self.has_enough_money(source_acctid,money)
            # 账户减钱
            self.reduce_money(source_acctid,money)
            # 账户加钱
            self.add_money(target_acctid,money)

            self.conn.commit()
        except Exception as e:
            self.conn.rollback()        # 事物回滚
            raise e


# 程序执行文件
if __name__ == "__main__":
    source_acctid = '1'
    target_acctid = '2'
    money = '100'

    # 连接数据库
    conn = pymysql.Connect(host='127.0.0.1', port=3306, user='root', password='123456', database='tencent', charset='utf8')

    tr_money = TransferMoney(conn)

    try:
        tr_money.transfer(source_acctid,target_acctid,money)
    except Exception as e:
        print ("Happen:" + str(e))
    finally:
        conn.close()

4. 全局解释器锁

GIL的全称为Global Interpreter Lock,全局解释器锁。

在 Python 语言的主流实现 CPython 中,GIL 是一个货真价实的全局线程锁,在解释器解释执行任何 Python 代码时,都需要先获得这把锁才行,在遇到 I/O 操作时会释放这把锁。

如果是纯计算的程序,没有 I/O 操作,解释器会每隔 100 次操作就释放这把锁,让别的线程有机会执行

有在做IO操作时,GIL总是被释放。对所有面对内建的操作系统C代码的程序来说,GIL会在这个IO调用之前被释放,以允许其它的线程在等待这个IO的时候运行。如果是纯计算的程序,没有IO操作,解释器会每隔100次或每隔一定时间15ms去释放GIL。

GIL对线程执行的影响
多线程环境中,python虚拟机按照以下方式执行:

  • 设置GIL
  • 切换到一个线程去执行
  • 运行代码,这里有两种机制:
    • 指定数量的字节码指令(100个)
    • 固定时间15ms线程主动让出控制
  • 把线程设置为睡眠状态
  • 解锁GIL
  • 再次重复以上步骤

python中GIL使得同一个时刻只有一个线程在一个cpu上执行,无法将多个线程映射到多个cpu上执行,但GIL并不会一直占有,它会在适当的时候释放

import threading
count = 0
def add():
    global count
    for i in range(10**6):
      count += 1
 
def minus():
    global count
    for i in range(10**6):
      count -= 1
 
thread1 = threading.Thread(target=add)
thread2 = threading.Thread(target=minus)
thread1.start()
thread2.start()
thread1.join()
thread2.join()
print(count)

# 结果
# -59452 
# 60868 
# -77007 

可以看到count并不是一个固定值,说明GIL会在某个时刻释放,那么GIL具体在什么情况下释放呢:

  1. 执行的字节码行数到达一定阈值
  2. 通过时间片划分,到达一定时间阈值
  3. 在遇到IO操作时,主动释放

相关文章

网友评论

      本文标题:python 一些常用的小技巧

      本文链接:https://www.haomeiwen.com/subject/puczpqtx.html