美文网首页Python学习
Python学习笔记-第9天:项目实战练习(4)+数据库编程

Python学习笔记-第9天:项目实战练习(4)+数据库编程

作者: 6d1bf2ffc4f3 | 来源:发表于2019-12-13 18:32 被阅读0次

    第九天 项目实战练习(4)+数据库编程

    今天计划继续坦克大战练习,学习项目及练习源码地址:
    GitHub源码

    坦克大战继续

    碰撞检测

    在游戏开发中,通常把显示图像的对象叫做精灵Spire精灵需要有两个属性 image要显示的图像,rect图像要显示在屏幕的位置。
    在Pygame框架中使用pygame.sprite模块中的内置函数可以实现碰撞检测。

    pygame.sprite.collide_rect(first, second) #返回布尔值
    

    pygame.sprite.Sprite是pygame精灵的基类,一般来说,总是需要写一个自己的精灵类继承pygame.sprite.Sprite。让坦克类、子弹类都继承编写的精灵类。

    音效处理

    music是pygame中控制流音频的pygame模块,音乐模块与pygame.mixer紧密相连, pygame.mixer是一个用来处理声音的模块,其含义为“混音器”。游戏中对声音的处理一般包括制造声音和播放声音两部分。使用pygame.mixer.music.load()加载一个播放音乐的文件pygame.mixer.music.play()开始播放音乐流。

    小结

    总算完成了,留有一点遗憾,就是敌方坦克在初始化的时候可能重叠坐标,这样会导致其无法移动(穿墙限制),应该很好处理,只是练习就愉快的跳过去了,我让敌方坦克可以穿透任意坦克,就行了,感觉这样挺有意思:)。需要看源代码的请在上一天的目录里面找地址,我就不贴出来了。

    花絮

    想发布自己的小程序怎么办?前面讲过如何打包,但是像游戏这样的有资源文件,Pyinstaller如何将资源文件一起打包到一个app中呢?
    基本原理:Pyinstaller 可以将资源文件一起bundle到exe中,当exe在运行时,会生成一个临时文件夹,程序可通过sys._MEIPASS访问临时文件夹中的资源

    官方说明:文档

    测试案例功能描述,访问资源文件夹res/a.txt,并打印其内容。实现方法如下:

    源码如下,比较简单,resource_path方法说明了如何使用sys._MEIPASS变量来访问临时文件夹中的资源

    #coding:utf-8
    import sys
    import os
    #生成资源文件目录访问路径
    def resource_path(relative_path):
        if getattr(sys, 'frozen', False): #是否Bundle Resource
            base_path = sys._MEIPASS
        else:
            base_path = os.path.abspath(".")
        return os.path.join(base_path, relative_path)
    
    #访问res文件夹下a.txt的内容
    filename = resource_path(os.path.join("res","a.txt"))
    print(filename)
    with open(filename) as f:
        lines = f.readlines()
        print(lines)
        f.close()
    

    结下来介绍如何生成可执行文件:

    首先需要生成spec文件,pyi-makespec -F test.py (如果要添加Icon等可以在这里就使用pyi-makespec --icon abc.jpg -F test.py语句生成spec文件)。编辑spec文件,在datas选项中说明需要将哪些文件加入exe,在零时文件夹中命名成什么即可。

    然后:pyinstaller -F test.spec

    数据库编程

    数据库的重要性不言而喻!
    Python支持多种数据库,从Python3.x版本开始,在标准库中已经内置了SQLlite3模块,它可以支持SQLite3数据库的访问和相关的数据库操作。在需要操作SQLite3数据库数据时,只须在程序中导入SQLite3模块即可。虽然SQLlite3用的比较广泛,但实际遇到更多的还是Mysql,MongoDB,Redis等

    SQLite3

    Python语言操作SQLite3数据库的基本流程如下所示。

    • 导入相关库或模块(SQLite3)。
    • 使用connect()连接数据库并获取数据库连接对象。它提供了以下方法:
      1. .cursor() 方法来创建一个游标对象
      2. .commit() 方法来处理事务提交
      3. .rollback() 方法来处理事务回滚
      4. .close() 方法来关闭一个数据库连接
    • 使用con.cursor()获取游标对象。
    • 使用游标对象的方法(execute()、executemany()、fetchall()等)来操作数据库,实现插入、修改和删除操作,并查询获取显示相关的记录。

    在Python程序中,连接函数sqlite3.connect()有如下两个常用参数。

    1. database:表示要访问的数据库名。
    2. timeout:表示访问数据的超时设定。
    • 使用close()关闭游标对象和数据库连接。数据库操作完成之后,必须及时调用其close()方法关闭数据库连接,这样做的目的是减轻数据库服务器的压力。

    一个样例代码: 示例

    Mysql

    在Python3中大家用的比较多的还是pymysql模块

    安装

    pip3 install pymysql

    基本使用

    # coding:utf-8
    import pymysql
    
    #关于中文问题
    #1. mysql命令行创建数据库,设置编码为gbk:create databse demo2 character set utf8; 
    #2. python代码中连接时设置charset="gbk"
    #3. 创建表格时设置default charset=utf8
    
    #连接数据库
    #charset和mysql服务端设置格式一样(还可设置为gbk, gb2312)
    conn = pymysql.connect(host="localhost", user="root", passwd="", db='test', charset='utf8', port=3306)  
    
    #创建游标
    cursor = conn.cursor()
    try:
        #执行sql语句
        cursor.execute("""create table if not exists t_sales(
                        id int primary key auto_increment not null,
                        nickName varchar(128) not null,
                        color varchar(128) not null,
                        size varchar(128) not null, 
                        comment text not null,
                        saledate varchar(128) not null)engine=InnoDB default charset=utf8;""")
                        
        # cursor.execute("""insert into t_sales(nickName,color,size,comment,saledate) 
                        # values('%s','%s','%s','%s','%s');""" % ("zack", "黑色", "L", "大小合适", "2019-04-20"))
                        
        cursor.execute("""insert into t_sales(nickName,color,size,comment,saledate) 
                        values(%s,%s,%s,%s,%s);""" , ("zack", "黑色", "L", "大小合适", "2019-04-20"))
        #提交
        conn.commit()
    
    
        ### 插入数据
        insert_sql = "insert into t_sales(nickName,color,size,comment,saledate) values(%s,%s,%s,%s,%s);"
        #返回受影响的行数
        row1 = cursor.execute(insert_sql,("Bob", "黑色", "XL", "便宜实惠", "2019-04-20"))
    
        update_sql = "update t_sales set color='白色' where id=%s;"
        #返回受影响的行数
        row2 = cursor.execute(update_sql,(1,))
    
        select_sql = "select * from t_sales where id>%s;"
        #返回受影响的行数
        row3 = cursor.execute(select_sql,(1,))
    
        delete_sql = "delete from t_sales where id=%s;"
        #返回受影响的行数
        row4 = cursor.execute(delete_sql,(4,))
    
        #提交,不然无法保存新建或者修改的数据(增删改得提交)
        conn.commit()
    
    
        ### 批量插入
        insert_sql = "insert into t_sales(nickName,color,size,comment,saledate) values(%s,%s,%s,%s,%s);"
        data = [("Bob", "黑色", "XL", "便宜实惠", "2019-04-20"),("Ted", "黄色", "M", "便宜实惠", "2019-04-20"),("Gary", "黑色", "M", "穿着舒服", "2019-04-20")]
        row1 = cursor.executemany(insert_sql, data)
        conn.commit()
    
        ## 查询数据
       
        select_sql = "select id,nickname,size from t_sales where id>%s;"
        cursor.execute(select_sql, (3,))
    
        row1 = cursor.fetchone()      #获取第一条数据,获取后游标会向下移动一行
        row_n = cursor.fetchmany(3)  #获取前n条数据,获取后游标会向下移动n行
        row_all = cursor.fetchall()  #获取所有数据,获取后游标会向下移动到末尾
        print(row1)
        print(row_n)
        print(row_all)
        # fetch获取的数据默认为元组格式,还可以获取字典类型的,需要修改cursor如下如下:
        # cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
    
    #conn.commit()
    except BaseException as e:
        print(e)
    finally:
        #关闭游标
        cursor.close()
        #关闭连接
        conn.close()
    

    注意execute执行sql语句参数的两种情况:

    execute("insert into t_sales(nickName, size)  values('%s','%s');" % ("zack","L") )   
    #此时的%s为字符窜拼接占位符,需要引号加'%s' (有sql注入风险)
    
    execute("insert into t_sales(nickName, size)  values(%s,%s);" , ("zack","L")) 
    #此时的%s为sql语句占位符,不需要引号%s
    

    多线程操控pymysql - 加锁

    对于pymysql模块,通过多线程操控数据库容易出错,得加锁串行执行。进行并发时,可以利用DBUtils模块来维护数据库连接池。

    import pymysql
    import threadind
    
    #**************************无连接池*******************************    
    # 每个线程都要创立一次连接,线程并发操作间可能有问题?
    def func():
        conn = pymysql.connect(host="127.0.0.1",port=3306,db="test",user="root",passwd="",charset="utf8")
        cursor = conn.cursor()
        cursor.execute("select * from user where nid>1;")
        result = cursor.fetchone()
        print(result)
        cursor.close()
        conn.close()
        
    if __name__=="__main__":
        for i in range(5):
            t = threading.Thread(target=func,name="thread-%s"%i)
            t.start()
    
    #**************************无连接池*******************************
    #创建一个连接,加锁串行执行
    from threading import Lock
    import pymysql
    import threading
    conn = pymysql.connect(host="127.0.0.1",port=3306,db="test",user="root",passwd="",charset="utf8")    
    
    
    lock = Lock()    
    def func():
        with lock:
            cursor = conn.cursor()
            cursor.execute("select * from user where nid>1;")
            result = cursor.fetchone()
            print(result)
            cursor.close()
            
            #conn.close()不能在线程中关闭连接,否则其他线程不可用了
            
    if __name__=="__main__":
        threads = []
        for i in range(5):
            t = threading.Thread(target=func,name="thread-%s"%i)
            threads.append(t)
            t.start()
            
        for t in threads:
            t.join()
        
        conn.close()
    

    多线程操作 - DBUtils连接池

    DBUtils连接池有两种连接模式:PersistentDB和PooledDB (官网文档:https://cito.github.io/DBUtils/UsersGuide.html

    模式一(DBUtils.PersistentDB):为每个线程创建一个连接,线程即使调用了close方法,也不会关闭,只是把连接重新放到连接池,供自己线程再次使用。当线程终止时,连接自动关闭。

    PersistentDB使用代码如下:

    #coding:utf-8
    
    from DBUtils.PersistentDB import PersistentDB
    import pymysql
    import threading
    
    pool = PersistentDB(
        creator = pymysql,  # 使用链接数据库的模块
        maxusage = None,    # 一个链接最多被重复使用的次数,None表示无限制
        setsession=[],     # 开始会话前执行的命令列表。如:["set datestyle to ...", "set time zone ..."]
        ping = 0,           # ping MySQL服务端,检查是否服务可用。# 如:0 = None = never, 1 = default = whenever it is requested, 2 = when a cursor is created, 4 = when a query is executed, 7 = always
        closeable = False,    # 如果为False时, conn.close() 实际上被忽略,供下次使用,再线程关闭时,才会自动关闭链接。如果为True时, conn.close()则关闭链接,那么再次调用pool.connection时就会报错,因为已经真的关闭了连接(pool.steady_connection()可以获取一个新的链接)
        threadlocal = None,    # 本线程独享值得对象,用于保存链接对象,如果链接对象被重置
        host="127.0.0.1",
        port = 3306,
        user = "root",
        password="",
        database="test",
        charset = "utf8"
    )
    
    def func():
        conn = pool.connection()
        cursor = conn.cursor()
        cursor.execute("select * from user where nid>1;")
        result = cursor.fetchone()
        print(result)
        cursor.close()
        conn.close()
        
    if __name__ == "__main__":
        for i in range(5):
            t = threading.Thread(target=func,name="thread-%s"%i)
            t.start()
    

    模式二(DBUtils.PooledDB):创建一批连接到连接池,供所有线程共享使用。

    (由于pymysql、MySQLdb等threadsafety值为1,所以该模式连接池中的线程会被所有线程共享。)

    PooledDB使用代码如下:

    from DBUtils.PooledDB import PooledDB
    import pymysql
    import threading
    import time
    
    pool = PooledDB(
        creator = pymysql,  # 使用链接数据库的模块
        maxconnections = 6,  # 连接池允许的最大连接数,0和None表示不限制连接数
        mincached = 2,   # 初始化时,链接池中至少创建的空闲的链接,0表示不创建
        maxcached = 5,   # 链接池中最多闲置的链接,0和None不限制
        maxshared = 3,   # 链接池中最多共享的链接数量,0和None表示全部共享。PS: 无用,因为pymysql和MySQLdb等模块的 threadsafety都为1,所有值无论设置为多少,_maxcached永远为0,所以永远是所有链接都共享。
        blocking = True,  # 连接池中如果没有可用连接后,是否阻塞等待。True,等待;False,不等待然后报错
        maxusage = None,   # 一个链接最多被重复使用的次数,None表示无限制
        setsession = [],   # 开始会话前执行的命令列表。如:["set datestyle to ...", "set time zone ..."]
        ping = 0,           # ping MySQL服务端,检查是否服务可用。# 如:0 = None = never, 1 = default = whenever it is requested, 2 = when a cursor is created, 4 = when a query is executed, 7 = always
        host="127.0.0.1",
        port = 3306,
        user="root",
        password="",
        database = "test",
        charset = "utf8"
    )
    
    def func():
        conn = pool.connection()
        cursor = conn.cursor()
        cursor.execute("select * from user where nid>1;")
        result = cursor.fetchone()
        print(result)
        time.sleep(5)  #为了查看mysql端的线程数量
        cursor.close()
        conn.close()
        
    if __name__=="__main__":
        for i in range(5):
            t = threading.Thread(target=func,name="thread-%s"%i)
            t.start()
    

    MongoDB

    暂时不学

    Redis

    暂时不学

    ORM

    暂时不学

    • sqlalchemy
    • async-sqlalchemy

    相关文章

      网友评论

        本文标题:Python学习笔记-第9天:项目实战练习(4)+数据库编程

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