文章目录
- python介绍
- 应用领域
- 环境搭建
- 基础知识
- 编程工具
- 变量
- 基本数据类型
- 容器数据类型
- 程序结构
- 运算符
- 函数
- 类
- 技巧总结
- python内存管理
- python常用技术
- python的缺陷优化
- python的编码规范
- 提升性能总结
python介绍
- 弱类型的语言
声明一个变量,直接赋值即可,简单方便,如 a = 10
强类型的语言,如C/C++就必须声明类型int a = 10; - 解释型语言
由python解释器,边解释边执行,将人类可理解的代码转为CPU可处理的0、1。 - 简单易学,易上手,高级语言,开发效率高,面向对象(一切皆对象)。
- 丰富的扩展库,拿来即用,不用重复的造轮子。
缺点:
- 运行效率低,比java慢一点,比C慢很多,但一般人是感知不到的。
- 代码无法加密,容易泄露核心源码
- 线程无法利用多核CPU资源
应用领域
web: Django/flask
爬虫:requests/scrapy/bs4/urllib等
数据分析:numpy/pandas/matplotlib
机器学习:sklearn
深度学习:tensorflow/pytorch/paddlepaddle
云计算:框架OpenStack
自动化运维、金融分析、量化交易等
谷歌、豆瓣、知乎、百度、apple等公司都在使用python。
环境搭建
- 最简单的方式就是安装一个python解释器
python官网 - 最方便的方式就是按照anaconda
anaconda安装 - 拷贝别人安装好的python程序及包,如python38整个程序包,放入C程序目录,配置环境变量
C:/xxx/xxx/python38;
C:/xxx/xxx/python38/Scripts;
C:/xxx/xxx/python38/Include;
最后记得更新pip,否则可能无法安装扩展包:
# 更新pip
python -m pip install --upgrade pip
基础知识
编程工具
- VScode
- Pycharm
- Anaconda
变量
- 字母、数字、下划线
- 不能使用保留的关键字 --keyword.kwlist
name = "jack" # 声明变量直接赋值,句尾没有分号;
my_address = "henan province" # 下划线命名法,官方推荐
myAddress = "zhengzhou" # 驼峰命名法,java等常用
NAME = "tom" # 全部大写,认为它是一个常量(其实也可以更改)
#多行注释使用 """ 三引号 """
# 单行注释 # 号
print(NAME)
#python shell中 交互式运行
#python xxx.py 以文件方式,python解释器解释执行
基本数据类型
int 、float、str、bool、None
name = "jack" # 字符串驻留,
age = 19 # 小整数池
height = 1.73
married = False # java/js/c/c++ true/false
girl_friend = None # 仅有一个
print("my name is %s, I am %d years old"%(name,age))
查看类型:type( obj )
转换类型:
int(1.4)–>1
int(True)–>1
int(False)–>0
float(2)–>2.0
str(2)–>“2”
str(True)–>“True”
str(None)–>“None”
字符串类型:可以使用单引号、双引号、三引号表示字符串
s1 = "my name"
s2 = "is jack"
print(s1+s2) # 字符串的拼接
print(s1*3) # 打印三遍
print(s1+5) #报错
容器数据类型
list/tuple/dict/set/str
- list列表
可变数据类型
# 定义一个列表,一系列数据的组合
list1 = ["a",23,True]
# 通过下标引访问
list1[0]
list1[-1]
list1[start:end:step] #分片复制
#修改
list1[0] = "张三"
#在ipython 下做一些测试、验证
#查看list1的类型
type(list1)
#查看list1 都由哪些属性、方法
dir(list1)
list1.tab键
#列表方法
list1.append() #追加一个元素
list1.extend(["d","e","f"]) #扩展一个列表
list1.insert(idx,e) #在指定索引处,插入数据
list1.pop()# 弹出末尾元素,并返回该元素
list1.pop(idx)#弹出指定索引的元素
list1.remove(e) #删除指定元素,顺序搜索,删除匹配到的第一个e
list1.sort() #默认升序排序列表本身,返回None
list1.reverse() #将所有元素逆序
list1.count(e) #统计列表中,元素e 出现的次数
list1.index(e) #查找e,返回对应的索引,找不到则异常
e in list1 #元素e是否在list1中,返回True/False
b = list1.copy() #复制
list1.clear() #清空元素
- tuple元组
不可变 数据类型
#索引操作 与列表相同,但是元组是不可变的
t = ("a","b","d")
#访问
t[0]
t[-1]
t[::1]
#修改
t[0] = "update" #异常
type(t)
dir(t)
- dict字典
key-value 键值对 如,{“name”:“jack”}
key有三种形式:字符串、数值、元组
#创建空字典
empty = {}
empty1 = dict()
d = {"name":"jack","age":23,"height":173.2,"sex":"Female"}
#查看类型
type(d)
d.keys() #所有的key,类似列表的对象
d.values() #所有的value类列表
d.items() #(key,value)元组项的类列表
#获取数据
d["name"] # key不存在时,报错
d["age"] = 29 #更新值
d.get("name",None) #获取"name"的值,没有这个key则返回None
d.get("addr","China")
#删除数据
d.pop("age") #弹出age 这个键值对,返回其值
d.popitem() #默认弹出最后一个键值对
del d["name"]
#从一个序列中设置key
d1 = {}
d1.fromkeys([1,2,3,"name"],"value")
#返回一个新字典{1:"value",2:"value",3:"value","name":"value"}
#更新,可以更新已有的key-value;也可以将一个新字典更新到d中
d.update({key:value})
d.clear() #清空自身
d.copy() #复制
- set集合
无序、去重的容器数据类型
s = set()
s1 = {1,2,3}
#查看类型
type(s)
dir(s)
s.tab键
#增加元素
s.add(e) #
s.remove(e)
s.pop()
#集合的操作
s1 = {1,2,3}
s2 = {2,3,4,5}
s1&s2 #交集 -->{2,3} s1.intersection(s2)
s1|s2 #并集 -->{1,2,3,4,5} s1.union(s2)
s1 - s2 #差集,属于s1,但不属于s2的元素-->{1}
s.clear()
s.copy()
s1.update(s2) # 将集合s2中的内容,更新到s1中
{2,3}.issubset(s1) # 是否s1的子集
程序结构
顺序、循环、分支结构
- 循环
# for 循环容器类型
for i in range(10):
print(i)
for i in [1,2,3]:
print(i)
for i in (1,2,3):
print(i)
#while
label = 1
while label < 5:
print("current label is %d"%label)
label += 1
- 分支
age = 23
if age > 50:
print("中老年人")
elif age > 30:
print("中年人")
else:
print("青年人")
运算符
# 算术运算符
+ - * / **(幂) //(地板除)
+= -= *= /=
#比较运算符
> >= < <= == !=
# 三目运算符
name = "jack"
a = name if name else "tom" # []/()/{}/""/None/0 均为False
#逻辑运算符
and or not # && || !
函数
# sum
def func(p1, p2, p3=None): # 位置参数必须传值, 关键字参数可以不传
if p3 is None:
return p1 + p2
return p1 + p2 + p3
# 收集参数
def func1(*args, **kwargs):
print("args:", args)
print("kwargs:", kwargs)
# 隐式返回None
类
# 定义类
class BenChi(object): # object 是一切类的基类
def __new__(cls, *args, **kwargs):
return object.__new__(cls)
def __init__(self, name, color):
# 实例属性
self.name = name
self.color = color
# 实例方法
def run(self):
print("start to run...")
# 静态方法
@staticmethod
def tell_brand():
print("奔驰品牌")
# 类方法
@classmethod
def cls_method(cls, *args):
print("当前类:", cls)
- __new__、__init__等魔法方法,python内置的用于完成对象的某种操作的方法;
- __name等为
私有
属性、私有
方法,只能在类内部使用; - _module等为模组内部
受保护
的变量,一般只模组内部使用; - class_等命名方式,为了不与内建的关键字冲突;
技巧总结
- python自省,程序运行时,获取变量的类型与结构,然后决定进一步的操作。
- type查看类型;如type(a)
- dir查看属性、方法;如dir(a)
- id 获取内存地址;如id(a)
- hasattr(obj, attr)
- getattr(obj, attr)
- setattr(obj, attr, val)
- isinstance(obj, cls) / issubclass(cls1, cls)
- 强大的inspect自省
模块
;
- 模块
- 一个xxx.py文件就是一个模块,import xxx
- 多个模块 + __init__.py 放入一个目录,形成一个包
- 导包搜索顺序,【当前目录】-【PYTHONPATH】-【sys.path】
- 函数、类的使用
- IDE用于python开发,黑窗口用于测试、自省、查看文档;
- 案例:求1000以内的所有质数,然后逐个打印。
# 判断是否质数
def is_prime(n):
if n < 2:
return False
p = n - 1
while p >= 2:
if n % p == 0:
return False
p -= 1
return True
def calc_prime(n):
primes = []
for i in range(2, n+1):
if is_prime(i):
primes.append(i)
return primes
if __name__ == '__main__':
result = calc_prime(100)
print(result)
python内存管理
- 引用计数;
变量赋值,引用计数+1;
删除变量,引用计数-1;
import sys
sys.getrefcount(obj) # 计算引用计数,本函数也会令引用计数+1
- 垃圾回收,引用计数为0则回收;
- 分代回收(标记-清除),解决循环应用问题;gc模块
import gc
gc.disable()
# 循环引用
-
小内存池机制;512字节以内的为小内存块,python独自管理,不返回OS。
-
内存优化,提高效率
- 小整数池,
[-5, 256] / id查看地址 / is判断是否同一个对象 - 字符串驻留,
由字母、数字、下划线组成的字符串,优先查找缓存池,找到则引用,未找到则创建并存入缓存池。 - 生成器 ,保存算法,节约内存
- 小整数池,
# 求20000内的质数
# 使用内置函数filter优化,使用psutil查看内存差异
python常用技术
- 闭包与装饰器
- 闭包,函数内部定义了另一个函数,
- 装饰器,闭包的应用,遵循
开闭原则
,扩展函数、类的功能。
# 1.创建任务队列,闭包内部函数添加任务
# 2.创建单次消费函数
# 3.装饰器扩展多次消费
import queue
import time
# 闭包,生产者
def outer(task_queue):
print("get queue:", task_queue)
def inner(url):
print(f"add task [{url}]")
task_queue.put(url)
return inner
# 多次消费高阶函数
def multi_consumer(func):
def wrapper(tsk_q):
while not tsk_q.empty():
func(tsk_q)
time.sleep(1)
return wrapper
# 消费者
@multi_consumer
def consumer(task_queue):
url = task_queue.get()
print(f"consuming [{url}]")
if __name__ == '__main__':
task_queue = queue.Queue(maxsize=10)
producer = outer(task_queue)
# 需要添加任务时调用
urls_list = ["http://www.baidu.com", "http://python.org", "https://cn.vuejs.org/"]
for url in urls_list:
producer(url)
# 消费任务
consumer(task_queue)
- 面向对象
- 单例模式
- 简单工厂模式
- 适配器模式
- 建造者模式
- 装饰器模式
# 单例模式
class Singleton(object):
instance = None
def __new__(cls, *args, **kwargs):
if cls.instance:
return cls.instance
cls.instance = object.__new__(cls)
return cls.instance
def __init__(self, name, age):
if hasattr(self, "init_"):
return
self.name = name
self.age = age
self.init_ = True
# 适配器模式
class HuaweiPay(object):
def huawei_pay(self, cash):
print("huawei pay:", cash)
class XiaomiPay(object):
def xiaomi_pay(self, cash):
print("xiaomi pay:", cash)
class PayAdapter(object):
def __init__(self, pay_method, func):
self.pay_obj = pay_method()
self.pay_func = func
self.check_func()
def check_func(self):
if not hasattr(self.pay_obj, self.pay_func):
raise AttributeError(f"{self.pay_obj} has no {self.pay_func} attr" )
def pay(self, cash):
getattr(self.pay_obj, self.pay_func)(cash)
- 线程, threading
- 轻量级进程,在一个进程内部创建;
- 操作系统可执行的最小单元;
- 一个线程可以创建其他子线程,多个子线程并发执行;
- 子线程基本没有自己的资源,而是共享进程中的内存等资源;
- cpython中由于GIL存在,多线程无法利用多核CPU;
- 适用于IO密集型程序,如爬虫;
多线程案例
在这里插入代码片
- 协程并发
- 协程又叫微线程,在
单线程
内部实现上下文的切换; - 单线程的高并发,避免多线程的创建、切换、销毁等资源和性能的消耗;
- 由程序员开发的,不是操作系统提供的;
- 适用CPU密集型程序;
- 无法利用多核CPU;线程内部阻塞,会阻塞整个程序;
协成案例
- 协程又叫微线程,在
# 异步下载图片
import aiohttp
import asyncio
from pathlib import Path
target_dir = Path("download") # 实例化一个path对象
target_dir.mkdir(exist_ok=True) # 创建目录
image_url = ["https://img0.baidu.com/it/u=695609245,3889806637&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=866",
"https://img2.baidu.com/it/u=1031222732,3723858324&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=889",
"https://img1.baidu.com/it/u=1724252144,3040470484&fm=253&fmt=auto&app=138&f=JPEG?w=764&h=500",
"https://img0.baidu.com/it/u=1195778458,70026656&fm=253&fmt=auto&app=138&f=JPEG?w=480&h=720",
"https://img2.baidu.com/it/u=1506702021,2130004389&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=749"]
async def fetch(session: aiohttp.ClientSession, url: str, name: str):
async with session.get(url, verify_ssl=False) as res:
data = await res.content.read() # 读取字节数据 耗时 挂起协程
(target_dir / name).write_bytes(data) # 写入字节数据
async def main():
# aiohttp.ClientSession 是异步上下文管理器
# 创建会话
async with aiohttp.ClientSession() as session:
# 创建协程对象的任务
tasks = [asyncio.create_task(fetch(session, url, name)) for url, name in zip(image_url, [f"image_{i + 1}.jpg" for i in range(5)])]
# 等待所有的任务执行完成
await asyncio.wait(tasks)
if __name__ == '__main__':
# 启动 事件循环,并自动关闭事件循环
loop = asyncio.get_event_loop() #
loop.run_until_complete(main()) #
python的缺陷优化
python的弱点就是执行效率低,开源的,不好加密
- 提高性能
- 内置函数(优化求质数)
- IO耗时,多线程&协程 异步 (异步爬虫)
- 计算耗时,C扩展 (优化斐波那契数列)
斐波纳契数列以递归的方法定义:
从第三项开始,每一项都等于前两项之和。
C扩展案例
# python实现
import time
# 斐波那契数列计算
def fibonacci(n):
if n == 0 or n == 1:
return 1
return fibonacci(n - 1) + fibonacci(n - 2)
def calc_perform(n):
start_time = time.perf_counter()
result = fibonacci(n)
end_time = time.perf_counter()
print("fibonacci result:", result)
print("total time: %.3fs" % (end_time - start_time))
if __name__ == '__main__':
calc_perform(35)
#
# fibonacci result: 14930352
# total time: 2.763s
# C实现
#include <stdio.h> //标准输入输出头文件
#include <time.h>
int fibonacci(int n) {
if (n == 0 || n == 1) {
return 1;
}
return fibonacci(n - 1) + fibonacci(n - 2);
}
// 定义一个返回int的主函数
int main() {
clock_t start_time = 0, end_time = 0;
start_time = clock();
int result;
result = fibonacci(35);
end_time = clock();
printf("fibonacci result: %d\n", result);
printf("total time: %.3f\n", (double)(end_time - start_time) / CLOCKS_PER_SEC);
// 函数体结束
return 0;
}
# 编译
gcc -fPIC xxx.c -shared -o xxx.dll
用C 实现计算逻辑,python调用C的函数。
- 源码保护
- Cython加密 (演示源码加密)
Cython加密案例
- Cython加密 (演示源码加密)
from setuptools import setup
from Cython.Build import cythonize
setup(ext_modules=cythonize(["coroutine.py", ], language_level=3), script_args=["build_ext", "--inplace"])
然后命令下执行python setup.py
python的编码规范
- 模块名、函数名、变量名,使用小写单词+下划线方式命名;
- 类名使用大驼峰方式,实例属性定义在构造函数中;
- 常量全部大写;
- 获取字典的值使用get;
- 缩进不能tab/space 混合使用;
- 导入包、模块不使用*
…
提升性能总结
- 尽量小整数池、字符串驻留等内置的内存优化方案解决问题;
- 不用的对象及时清除引用,避免循环引用;
- 巧用生成器节约内存。
- IO密集型,使用多线程;
- CPU密集型,使用协程;
- 终极性能提升使用C扩展。