本文持续更新…

我的目录

    • 说明
    • 第一部分:python基础知识
      • 0 python风格
        • 0.1 命名风格
        • 0.2 空行与空格
          • 0.2.1 空格和tab
          • 0.2.2 类中的空行问题
      • 1 字符串,string
        • 1.1 字符串大小写
        • 1.2 合并字符串'+'
        • 1.2 空白
        • 1.3 删除空白strip()
        • 1.4 转义字符'\'以及路径中的r
        • 1.5 字符串拆分成列表split()
        • 1.6 .join()合并
        • 1.7 list()单词拆分成字母
        • 1.8 查看数据类型及修改类型
      • 2 数学运算符(加减乘除取余取整幂次)
      • 3 注释
        • 3.1 del,reset删除和查看变量
      • 4 列表
        • 4.0 数组
          • 4.0.1 squeeze()删除大小为1的维度
          • 4.0.2 反序[::-1]
          • 4.0.3 数组行/列调换顺序
        • 4.1 修改,添加,删除, 弹出元素等
        • 4.2 排序,确认长度等
          • 4.2.1 永久排序sort()
          • 4.2.1 * (补充)numpy排序和倒序
          • 4.2.2 临时排序sorted()
          • 4.2.3 倒着打印列表reverse()
          • 4.2.4 返回某个数在列表中的数量count()
          • 4.2.5 返回元素的索引值index()
          • 4.2.6 返回最大值,最小值以及对应的索引max(),index(max())
          • 4.2.7 拷贝列表x到y范围a[x:y]
            • a[x:y]拷贝的应用及常见错误
            • 赋值,深浅拷贝copy(),deepcopy()和a[x:y]
          • 4.2.8 集合去除重复元素set()
        • 4.3 列表相加
        • 4.4 初始化矩阵--初始化列表
      • 5 元组
        • 5.1 元组概况
        • 5.2 多维元组
      • 6 字典
        • 6.1 空字典
        • 6.2 删除键值对del
        • 6.3 遍历字典
        • 6.4 将列表变为字典
      • 7 实参和形参
        • 7.1位置实参
        • 7.2关键字实参
        • 7.3 参数设置默认值--默认参数
        • 7.4 传递任意数量的实参\*星号\*args---建立空元组
          • 7.4.1 只有`*`星号的形参
          • 7.4.2 同时有`*`星号和位置实参
          • 7.4.3 同时有`*`星号和位置实参
        • 7.5 传递任意数量的键值对实参---两个星号**---建立空字典\*\*kwargs
        • 7.6 参数调用常见错误
          • 错误1:SyntaxError: positional argument follows keyword argument
          • 错误2:non-default argument follows default argument
        • 7.7 学会在参数中使用None
      • 8类
        • 8.0 类的调用
        • 8.0* 函数调用
        • 8.1修改类中属性的三种方法
          • 1,直接修改属性的值
          • 2,通过方法改变属性的值
          • 例题,禁止属性往回调
          • 8.1.3 对属性的值递增
        • 8.2 类的继承和调用
      • 9 文件读取和异常try-except处理
        • 9.1 文件读取和写入--open方法
        • 9.2 json读取和写入
        • 9.3 异常
        • 9.4 举例:1,输入用户名验证是否存在
      • 10 os.path路径问题
        • 10.1 os.path.dirname和os.path.dirname(\_\_file\__)返回路径目录
        • 10.2 os.path.join()路径拼接
      • 11 input输入sys.stdin输入和print输出
      • 12 @符号--装饰器decorator和计时器time.time
      • 13 矩阵矢量数组等乘法
      • 14 文件保存
      • 15 time计时
      • 16 全局变量和局部变量
      • 16python是动态语言
      • 17 使用latex标注
        • 17.1 plt中的latex
        • 17.2 getdist中的latex
      • 18, 压缩解压文件
        • 18.1 常规方法:
          • 1)使用tarfile
          • 2)使用zipfile
        • 18.2 使用patool
      • 19, python其它常见函数
        • 19.1 isinstance()函数
        • 19.2 assert函数
      • 20 matplotlib画图
        • 20.1 subplot子图
      • 21 插值
        • 21.1 numpy线性插值numpy.interp()
        • 21.2 scipy插值, scipy.interpolate()
          • 21.2.1一维插值
          • 21.2.2 二维插值interp2函数--平面上所有点
          • 21.2.3 二维插值griddata函数---插值对应的(x,y)坐标的点
          • 21.2.4 例题
        • 21.3 [matlab进行插值计算](https://blog.csdn/Mr_Cat123/article/details/107596863)
      • 22 积分
        • 22.1一重积分
        • 22.2 二重积分
        • 22.3 多重积分
        • 22.4 被积函数含有其它变量
        • 22.5 不使用匿名函数求积分
          • 22.5.1 一重积分
          • 22.5.2 二重积分
          • 22.5.3 被积函数含有多个函数
      • 23 any()和all()
      • 24 input和sys.stdin输入
      • 25 format格式化函数
        • 25.1 科学计数法输出
    • 第二部分:python应用项目
      • 1 Web应用程序---Django入门
        • 1.1 创建项目
      • 2 python生成动态视频
      • 3 将新数组插入原始数组
    • 第三部分:python常见错误
      • 1函数调用之后原始值变化
      • 2,cmb_map() missing 1 required positional argument: 'self'
      • 3 Unable to allocate array with shape (5, 432, 360, 3000) and data type float64内存不够
    • 第四部分:python巧妙应用
      • 1 找出列表/数组/元组中某个字符的index及其拓展
        • 1.1 返回某个字符的index
        • 1.2 拓展:找出某个列表/元组中的某个子元组
      • 2,通过split设置输出文件名
    • 第五部分:python例题
      • 1, 变位词判断
      • 2, 判断数组、列表、字符串是否为空(空数组,空列表,空字符串)
    • 第六部分:python提高运算速度的方法
    • 第七部分:数据结构与算法
      • 1 栈stack
        • 1.1 python实现栈的功能
      • 2 队列queue
        • 2.1 python实现队列的功能
      • 3 递归
        • 3.1 递归给序列求和
        • 3.2 递归实现进制转换
        • 3.3 递归调用turtle内置包画图
        • 3.4 递归实现阶乘
        • 3.5 递归常见错误RecursionError
    • 第八部分: 常用模块
      • 1 numpy
        • 1.1 numpy random生成随机数
        • 1.2 numpy多维数组取反(反向排序,逆序)
      • 2 sympy
        • 2.1 符号计算
    • 第九部分:数学基础
        • 1 数据平滑处理
        • 2 数据拟合fit(多项式拟合)
    • 第十部分:机器学习
      • 10.1 tensorflow

说明

本博文作为笔者笔记使用,不会很全面(主要课本是:python编程从入门到实践)

第一部分:python基础知识

0 python风格

0.1 命名风格

通常python命名只能使用字母,数字和下划线

  • 变量名:1)可以以字母或下划线开头,不能以数字开头。2)变量名中间不能有空格如’my car =’ 3)不能使用python已有的关键字和变量名 4)尽量简短而具有描述性:name比n好,name_length比length_of_persons_name好
  • 常量: 为了区分常量和变量,通常用大写字母表示常量,如NAME=‘Dylan’,如果有多个单词,用下划线分开,如MY_NAME=‘Dylan’
  • 类:建议使用驼峰命名法—即单词首字母大写,不使用下划线。如类ElectricCar(),这个类的两个单词首字母都大写,中间不用下划线。
  • 实例,模块和类下定义的函数/方法:都使用小写字母格式,并且单词之间用下划线。所谓实例即调用函数或者类进行实例化,而模块即为.py文件。如一个名为electric_car.py的模块(文件)里面有一个Car()类,类里面有一个get_car_model()的函数(方法)。
    如:模块electric_car.py文件如下
'''类'''
class ElectricCar(): #驼峰命名法
     '''函数/方法'''
	def get_car_model(self):
		pass

my_car = ElectriCar()  #实例

0.2 空行与空格

0.2.1 空格和tab

一定要注意空格与tab的区别,出现unindent does not match any outer indentation level的错误提醒是因为在函数或者类中有四个空格又有tab,这两者是不能同时出现来混用的(也就是,四个空格不等于一个tab),所以要么全部用空格,要么全部用tab

0.2.2 类中的空行问题

在一个类中,通常使用一个空行来分离不同的方法;而在一个模块中(一个文件,如car.py),通常使用两个空行来分离不同的类。一般而言,在class Car():类定义完之后,使用’’‘xxx’’‘来描述类的作用,如果不加描述,也空一行再定义初始化’def __init__()’

1 字符串,string

1.1 字符串大小写

字符串大小写由 title(),upper(),lower() 函数解决

my_str = "hello world"
print(my_str.title())   #首单词大写
print(my_str.upper())   #全部大写
print(my_str.lower())   #全部小写

Hello World
HELLO WORLD
hello world

1.2 合并字符串’+’

使用“+”号完成

surname = "Wu"
givename = "DL"
full_name = surname+' '+givename  #单引号中间空一格
print(full_name)

Wu DL

1.2 空白

空白只首行空四格(\t, tab),换行(\n)

surname = "Wu"
givename = "DL"
full_name = surname+'\t'+givename
print('hello\n'+full_name)

hello
Wu      DL

1.3 删除空白strip()

使用strip函数(strip:/strɪp/,除去,去掉)

surname = " Wu"  #左边有一个空格
givename = "DL "  #右边有一个空格
full_name = surname+' '+givename 
print(full_name)  
print(full_name.rstrip())
print(full_name.lstrip())
print(full_name.strip())

 Wu DL         #左右两边都有一个空格(我将图片展示在下,蓝色是字符部分)
 Wu DL         #左边有一个空格,右边没有
Wu DL          #左边没有空格右边有一个空格
Wu DL          #左右两边都没有空格


注意:用strip函数删除空格是没有改动原始存储中的字符串的,也就是说当再次print(full_name)的时候空格是依然在的。如果要彻底去掉空格,并将原始的存储中的空格也去掉,可以重新在定义。

surname = " Wu"
givename = "DL "
full_name = surname+' '+givename
print(full_name.strip())
print(full_name)
full_name = full_name.strip() #重新再定义
print(full_name)

Wu DL   #左右都没有空格
 Wu DL   #左右都有空格,没有改变原始存储
Wu DL    #左右都没有空格

应用:这种去空格方法最常用于在用户输入用户名时,如果电脑想要记住用户名,那么需要预处理,预处理即去掉用户可能输入的空格。

1.4 转义字符’'以及路径中的r

转义字符“\”

目的:输出“Hello”,有引号
print(""Hello"")  #企图用4个引号
print("\"Hello\"")   #在\后的第一个符号是被转义了,即第2,3个被转义了。

    print(""Hello"")
                ^
SyntaxError: invalid syntax  #第一种方法语法错误

"Hello"   #正确

在输入路径时,如

data = np.loadtxt(r'\user\my_file.txt')

通常在路径前面加了r,因为有时候,特别是windows会将路径中的斜杠理解为转义字符,为了避免这种冲突,将r加在路径的最前面。

1.5 字符串拆分成列表split()

sent = 'my name is wudl'
print(sent.split())

['my', 'name', 'is', 'wudl']

按其他字符划分split(‘字符’),如

xxx.split('\\') 按\\来划分


应用见下面的第四部分第二节

1.6 .join()合并

参考博文join函数

1.7 list()单词拆分成字母

1.8 查看数据类型及修改类型

python数据可以通过type查看类型,如果是用numpy的话,那么可以通过.dtype()来查看,通过astype()来修改;(注意,从float64降低到float32,保留的小数位数降低了;numpy默认是float64。但是float64消耗的存储空间是float32的两倍;比如一个大数据float64文件是4G的,那么float32则是2G,float16是1G;同时在读取到内存的时候,float64也是占了4G内存,可能让电脑变慢,好处就是精度高。)

2 数学运算符(加减乘除取余取整幂次)

  • 加:+
  • 减:-
  • 乘:*
  • 除:/
  • 幂:**
  • 取整除://
  • 取余数(求模运算符):%

3 注释

注释的意义:强烈建议新手养成写注释的习惯,两方面1)阐述代码要做什么 2)阐述如何做(当然建议项目加一个README.txt文件)

  • 单行:#
  • 多行:前后各3个单/双引号

3.1 del,reset删除和查看变量

查看变量
我们进入ipython之后,可能前面输入的变量名忘记了,想查看有多少变量,此时可以通过dir(),或者globals().keys(), locals.keys()等,这个时候是输出的所有变量,里面包含我们定义的变量名和其它python的变量。另外,可以查看一个包里面的变量,比如

import numpy as np
print(dir(np))

删除变量
为了释放内存空间,我们会将一些赋值较大的变量释放掉,可以通过del来完成

del xxx   #释放变量xxx的内存

如果我们想一次性释放所有变量,那么可以通过reset

reset
Once deleted, variables cannot be recovered. Proceed (y/[n])? #选择y

清除控制台历史命令

clear

4 列表

4.0 数组

4.0.1 squeeze()删除大小为1的维度


squeeze在matlab中的用法跟python一样
可以查看官方描述,举例:

4.0.2 反序[::-1]

见第八部分1.2节。

4.0.3 数组行/列调换顺序

比如有个四维数组a,(4,22,1000,2).现在要调换最后一维的两列的顺序,使用numpy可以通过索引可以直接调换

4.1 修改,添加,删除, 弹出元素等

修改

names = ['my','name','is','wudl'] #通常列表是有多个元素,因此名为为复数形式
names[0] = 'her'  #修改列表第一个元素
print(names)

['her', 'name', 'is', 'wudl']

添加append()

names = ['my','name','is','wudl']
names.append('her') #在末尾增加元素
print(names)

['my', 'name', 'is', 'wudl', 'her']

插入insert()

names = ['my','name','is','wudl']
names.insert(0,'her')
print(names)

['her', 'my', 'name', 'is', 'wudl']

删除del

names = ['my','name','is','wudl']
del names[0]
print(names)

['name', 'is', 'wudl']

根据名字移除remove()

names = ['my','name','is','wudl']
names.remove('name')
print(names)

['my', 'is', 'wudl']

默认情况下,remove删除第一个期望的数,如要删除1

值得注意的是,如果要删除所有的1,则不能用循环再采用remove一个个删除,因为remove删除一个数之后排序会改变(排序减1,因为已经删除了一个数了,列表变短,但remove删除是按照索引顺序进行的,这就使得有些数没被删(因为索引变了)


可以看到最后还有个1没被删。
我们可以采用先找出1所在的索引,然后再通过np.delete来删除(np.delete可以删除行和列,具体看官网),找索引可以看后面的章节,这里直接给全部代码

弹出pop()

这个函数在应用中很有帮助,这个函数默认弹出最后一个元素,因此比如在购买东西的时候,可以知道最后一个是什么,直接弹出来即可;此外还可以直接用弹出的元素,显示它的一些特征,如价格等。

names = ['my','name','is','wudl']
last_ele = names.pop()
print(names)
print(last_ele)

['my', 'name', 'is']
wudl

弹出任意位置的元素

names = ['my','name','is','wudl']
last_ele = names.pop(1)
print(names)
print(last_ele)

['my', 'is', 'wudl']
name

4.2 排序,确认长度等

4.2.1 永久排序sort()

按字母正顺序

names = ['my','name','is','wudl']
names.sort()
print(names)

['is', 'my', 'name', 'wudl']

值得注意的是,上面names.sort()排完之后names列表自动变成排完之后的顺序,原始的顺序已经被覆盖。

  • 1, 如果有大小字母,那么先排序大写字母,再排小写字母(因为大写字母的ASCII值要小)
names = ['My','Name','is','wudl']
names.sort()
print(names)

['My', 'Name', 'is', 'wudl']
  • 2, 如果第一个字母相同,那么开始对比第二个字母,第三个,。。。
a = ['abd','abc']
a.sort()
>>> a = ['abc', 'abd']
  • 3, 如果第一个字母相同,第二个字母一个存在一个不存在,那么不存在的排前
a = ['ab','abc']
b = ['ab', 'ab '] #空格
a.sort()
b.sort()
>>>['ab', 'abc']
>>>['ab', 'ab ']

按字母反顺序,加参数reverse=True(实际上是找到ASCII值,然后从大到小排列)

names = ['my','name','is','wudl']
names.sort(reverse=True)
print(names)

['wudl', 'name', 'my', 'is']

有大写字母的情况

names = ['My','Name','is','wudl']
names.sort(reverse=True)
print(names)

['wudl', 'is', 'Name', 'My']

**补充:**sort()函数有个key功能,这个功能很强大,比如我们要将数组按照长度排序,那么令key=len即可,如b数组是一个长度没什么顺序的,现在要将b中的数组按照从短到长排序:


如果reverse=True,则按照从长到短排序。

4.2.1 * (补充)numpy排序和倒序

排序

np.sort(array)

逆序
在排完序之后,使用[::-1],这个[::-1]的含义请参考[::-1]的含义

字符串倒序
下面使用两种方法,一个是[::-1],另一个是使用reverse函数

4.2.2 临时排序sorted()

希望排完序之后对原始列表无影响

names = ['my','name','is','wudl']
print(sorted(names))#注意是sorted(names)不是names.sorted()

['is', 'my', 'name', 'wudl']
4.2.3 倒着打印列表reverse()

注意这里说的是直接将列表反转,不是按字母顺序反排序

names = ['my','name','is','wudl']
names.reverse()
print(names)

['wudl', 'is', 'name', 'my']
4.2.4 返回某个数在列表中的数量count()

返回列表中’wudl‘的数量

names = ['Dylan','wudl','Wudl','WUdl','Asle','Abdul']
print(names.count('wudl'))

1

可以看到这是区分大小写的,但是如果希望WUdl,wudl, Wudl都是同样的,那么可以先将names统一小写,然后在计数

names = ['Dylan','wudl','Wudl','WUdl','Asle','Abdul']
names_low = [name.lower() for name in names]
print(names_low)
print(names_low.count('wudl'))

['dylan', 'wudl', 'wudl', 'wudl', 'asle', 'abdul']
3
4.2.5 返回元素的索引值index()

返回第一次出现时的索引值

names = ['my','name','is','wudl','name']
print(names.index('name'))

1  

可以看到,当一旦找到第一个name之后就结束了,不再找第二个;

返回所有指定的元素的索引
可以借助enumerate(枚举)函数,enumerate函数的用法见enumerate的用法

names = ['Dylan','wudl','Willion','Abdul','wudl']
indexs = [index for index,name in enumerate(names) if name=='wudl']
print(indexs)

[1, 4]

如果是numpy类型,更简单的方法是

  • 整型np.where(yourarr, value)
  • 浮点型np.where(np.isclose(yourarr,value))
  • 这两个方法同样适用于高维数组
4.2.6 返回最大值,最小值以及对应的索引max(),index(max())
nums = [9,1,20,8,4,22,40,20]
print(max(nums))
print(min(nums))
print(nums.index(max(nums)))

40
1
6
4.2.7 拷贝列表x到y范围a[x:y]

通过[x:y]设定拷贝的起始索引x和最后索引y,以下是跟直接赋值的差别。


输出a,b,c的id

可以看到赋值没有产生副本,而是关联到列表中,建立索引联系(类似链接);而使用[:]拷贝则产生了新的副本,可以对副本进行操作而不影响以前的列表
同样不使用append,使用改变值的方式

可见对副本列表没有改变。
下面考虑数组是否有这样的功能
注意,由于数组没有append功能,以下我通过改变第一个值替代append

import numpy as np 

a = np.array([1,2,3,4])
b = a
c = a[:]
a[0]=9
print(a)
print(b)
print(c)



虽然c的id不一样,但是依然会改变c副本的值。因此对于数组而言,在使用[x:y]进行拷贝副本时可以先转为列表再拷贝,完成之后再转为数组,如下:

import numpy as np 

a = np.array([1,2,3,4])
b = a
c = list(a)[:]
a[0]=9
print(a)
print(b)
print(c)


此外可以参考下一节的深浅拷贝,可以通过拷贝来实现(为了防止有多层数组,建议使用深拷贝):

import numpy as np 
import copy

a = np.array([1,2,3,4])
b = a
c = copy.deepcopy(a)
a[0]=9
print(a)
print(b)
print(c)


字典没有a[:]这种功能,不考虑字典
总结:对于a[x:y]产生副本而言,只对list生效

a[x:y]拷贝的应用及常见错误

拷贝在函数中的使用,当一个列表在函数中使用,而函数改变了原始列表后,原始列表将不复存在;而我们想要保留原始列表,方便后续使用时,可以使用[:]拷贝副本,注意,数组则不能如此

def new_list(list_):
    new_num = 8
    list_.append(new_num)
    return(list_)

origin_list = [1,2,3]
print('new_list= ',new_list(origin_list))
print('origin_list= ',origin_list)


可以看到原始列表改动了

def new_list(list_):
    new_num = 8
    list_.append(new_num)
    return(list_)

origin_list = [1,2,3]
print('new_list= ',new_list(origin_list[:])) #输入到函数中的是列表副本
print('origin_list= ',origin_list)


通过副本的形式没有改变原始列表。
此外关于拷贝可以参考下一节。
数组不能如此,数组会改变原值,举例:

a = [1,2,3,4]
b = a[:]
print(id(a))
b[0] = 5
print(a,b,id(a))
168394120
[1, 2, 3, 4] [5, 2, 3, 4] 168394120

而数组为

a = np.array([1,2,3,4])
b = a[:]
print(id(a))
b[0] = 5
print(a,b,id(a))
168304688
[5 2 3 4] [5 2 3 4] 168304688

常见错误

import numpy as np

def square(a):
    a[0] = a[0]+1  #给形参的第一个值+1
    return a**2
    
val = np.array([1,2])
print(square(val))
print(val)

将数组val直接传递给函数square想求平方,结果发现val的原始值变了,结果如下

[4 4]  #求平方之后
[2 2]  #val的值变了

所以要想val的值不变就需要考虑到拷贝的问题,可以使用深拷贝copy.deepcopy(),或者因为这里只有一层,所以也可以使用浅拷贝copy.copy(),但是不能使用val[:],因为这是数组不是列表,会改变原始值
所以上面的内容改为

val = np.array([1,2])
#print(square(val[:]))这样的结果会改变原始的val值
print(square(copy.copy(val)))
#也可以使用copy.deepcopy(val)
print(val)
[4 4]
[1 2]
赋值,深浅拷贝copy(),deepcopy()和a[x:y]

关于a[x:y]参考上一节。
详细内容可以参考这篇博文:Python.可变与不可变类型,传递,拷贝的整理:这篇博文在说浅拷贝时不正确,因此深浅拷贝可以参考下面这个博文深浅拷贝

这里进行简化和总结:
在说赋值和深浅拷贝之前,需要了解一下可变和不可变类型
可变类型(mutable)包括:列表,数组,字典
不可变类型(unmutable)包括:数字,字符串,元组,bool,None
解释:

  • 可变类型----以列表为例,a=[1,2,3,4],(数组a=np.array([1,2,3,4])与列表一样)现在改变元素a[0]=5,则a变为[5,2,3,4],但是a的地址是不变的(可以通过id(a)来查看地址),变的是里面的元素,所以叫做可变类型。字典跟列表相同。
  • 不可变类型----以字符串为例,a=Wudl,现在改为a=Dylan,这里并不是将Wudl改为Dylan,而是重新开辟了一个内存空间,空间里面放的值为Dylan,地址已经改动了。所以是值不能变,变的是开辟了新的空间

  • **补充:**地址id相当于房间号,内存空间相当于房间,对于不可变类型而言,事先为这类型开辟了空间,所以id已经变了。而对于可变类型而言,是同一个房间,相当于一个房间里面有四个电话,每个电话又分别联系到四个其他房间,这四个房间放的值为1,2,3,4

赋值: 数据完全共享。将a赋值给b,那么a和b的值是一样的,a和b指向同一个内存地址

  • 对于不可变类型而言,如下图所示,当重新给a赋新的值时,b还是以前a的值。
a = 'wudl'
b = a
print(id(a),id(b))
a = 'dylan'
print(a,b)
print(id(a),id(b))


可见一开始a,b的地址是一样的,当a赋新值之后地址变了,值也变了,因为a指向了新的值。

  • 对于可变类型而言,以列表为例
a = [1,2,3,4]
b = a
print(id(a),id(b))
a.append(5)
print(a,b)
print(id(a),id(b))


当给a增加一个值时,可以看到a,b都增加了,因为没有开辟新的空间,而是在原来的房间上增加了一个新的电话指向5这个值(可以id(a[4])查看这个5的房间号);
同理,数组如下:

import numpy as np 

a = np.array([1,2,3,4])
b = a
print(id(a),id(b))
a[0]=8
print(a,b)
print(id(a),id(b))


字典如下:

a = {'a':5,'b':8,'c':9}
b = a
print(id(a),id(b))
a['a']=0
print(a,b)
print(id(a),id(b))


如果是开辟新的空间的话(不在以前的列表/字典/数组上改变)那么得到的结论是跟不可变类型一样的,因为是把列表看成一个整体

a = [1,2,3,4]
b = a
print(id(a),id(b))
a = [5,6,7,8]
print(a,b)
print(id(a),id(b))


深浅拷贝:参考博文深浅拷贝
浅拷贝指仅仅拷贝数据集合的第一层数据,深拷贝指拷贝数据集合的所有层。所以对于只有一层的数据集合来说深浅拷贝的意义是一样的,比如字符串,数字,还有仅仅一层的字典、列表、元祖等.
浅拷贝copy

解释上图:浅拷贝表示将n1拷贝给n3时,第一层都拷贝了,可以理解为第一层的地址完全拷贝给n3,而第二层以后的都没有拷贝给n3,仅仅是将值拷贝了。类似于一个n1是一个大房间里面有3个电话,每个电话指向一个房间,共3个房间,这些房间有的是直接放值的,有的又指向了另外的房间。所以浅拷贝表示另开了一个内存空间,拷贝了第一层的数据,即拷贝了3个电话,这3个电话指向3个房间的,因此表示拷贝了第一层的3个房间。注意n3是另开空间,所以这3个房间的值在n1中改变之后,在n3是不会改的。但后面的层的值都没有拷贝过来而是直接指向以前的内存地址,所以这些房间的值一旦改变了n3也会改变,举例如下

import copy
‘’‘
一个房间n1里面有三个电话k1,k2,k3,这三个电话指向另外三个房间
房间里的值为wu,123和指向另外两个房间,这另外两个房间的值
为alex,678
所以浅拷贝只拷贝了第一层的值,剩下的都是直接指向以前的内存地址
’‘’
n1 = {'k1':'wu','k2':123,'k3':['alex',678]}
n3 = copy.copy(n1) #浅拷贝
‘’‘
改变第一个房间k1的值,n3是不会改变的,因为已经
另开空间拷贝这些值了
’‘’
n1['k1'] = 1  
‘’‘
第三个房间K3指向的第一个房间改为’000‘,
这样n3会跟着改,因为n3只是指向它,所以
房间值改了n3也会改
’‘’
n1['k3'][0] = '000'
print(n1,n3)

结果如下(因为字典是没有先后顺序的,所以顺序会改动)

{'k3': ['000', 678], 'k1': 1, 'k2': 123} 
{'k3': ['000', 678], 'k1': 'wu', 'k2': 123}

深拷贝:属于完全备份,开辟了一个新的空间来备份所有的值,所以更改n1对n3不会有任何改动,代码同上,只将copy.copy()改为deepcopy()

import copy

n1 = {'k1':'wu','k2':123,'k3':['alex',678]}
n3 = copy.deepcopy(n1) 

n1['k1'] = 1  

n1['k3'][0] = '000'
print(n1,n3)

结果为:

{'k3': ['000', 678], 'k1': 1, 'k2': 123} 
{'k3': ['alex', 678], 'k1': 'wu', 'k2': 123}
4.2.8 集合去除重复元素set()

集合在某些场合很重要,可以去除重复的元素。比如在计数的时候,重复的元素算一次,则set显得很重要

4.3 列表相加

列表a,b直接相加表示在列表a末尾添加列表b,

因此不是元素相加,相当于给列表a拓展上列表b,因此也可以使用extend方法:

这跟数组相加不一样,数组相加表示对应元素相加

4.4 初始化矩阵–初始化列表

有时候我们需要初始化一个列表或者矩阵,以方便后续操作。
在不调用numpy包的情况下完成这个任务。
比如初始化一个6行4列的矩阵,里面的值都为0.

mat_a = [[0]*3 for _ in range(6)] #_是临时变量,后续都用不到
or
mat_a = [[0]*3 for i in range(6)]


注意,不能使用如下命令

mat_a = [[0]*3]*6

因为这样会将里面每行都指向同一个ip,导致改变一行时其他行业会同样改变。

5 元组

5.1 元组概况

元组的要点只有两点:

  • 元组里的元素不可改变
  • 但是元组是可以重新赋值的



以下来自元组


5.2 多维元组

1 二维元组

上面都是二维的元组,第一维元组是(1,2),第二维元组是(3,4)以及后来的(3,4,5,6)。当两个维度一样长时可以写出来,如np.shape为(2,2),但后面那个不一样长,无法表达出来,就只写了是(2,)表示二维。

2 三维元组

6 字典

6.1 空字典

空字典及赋值

a = {}
a['color'] = 'green'
print(a)

{'color': 'green'}

在应用中,可以将用户的信息放在字典里,如
建立一个用户字典,输入名字和性别保存到字典中

users = {}  #建立空字典
active = True    #建立while的标志
while active:
    name = input('\nWhat is your name? ')
    gender = input('\nWhat is your sex/gender? ')
    #保存用户的性别,将性别对应保存到姓名中
    users[name] = gender
    #验证信息
    repeat = 'Please check your name and gender: '+name+': '+gender
    print(repeat)
    #检查是否你是最后一个输入,如果是则结束
    ans = input('\nAre you the last one? (yes/no)')
    if ans == 'yes':
        active = False
print('\n------results-------')
print(users)

6.2 删除键值对del

6.3 遍历字典

1键值对
items()函数将键值对输出

a = {'color':'green',
     'score':'10'}

for key, value in a.items():
    print('\nkey: '+key)
    print('\nvalue: ' +value)

>>>
key: score

value: 10

key: color

value: green


2遍历建

a = {'color':'green',
     'score':'10'}

for key in a.keys():
    print('\nkey: '+key)


3,遍历值
使用values()函数

6.4 将列表变为字典

x = ['a','b','c']
y = [1,2,3]
mydict = {}
for i in range(len(x)):
    mydict[x[i]] = y[i]
print(mydict)


更方便的是直接调用库函数

x = ['a','b','c']
y = [1,2,3]
mydict = dict(zip(x,y))
print(mydict)

例:有两个列表,x表示横坐标,y表示纵坐标,要求由x可以得到y。
可以将x,y变为字典,由字典对应的x输出y。

7 实参和形参

  • 在函数定义中,括号()里的参数是形参----函数完成工作所需要的信息
  • 在函数调用中,括号()里的参数是实参----调用函数时传递给函数的信息

7.1位置实参

通过位置一一对应来调用的方式,所以位置顺序很重要如下面的代码

def personal_message(name,gender):   #形参
    print('my name is '+name+', and my gender is '+gender)
    
personal_message('wudl','male')  #实参的位置跟上面形参的对应,wudl对应name,male对应gender

>>>
>my name is wudl, and my gender is male

7.2关键字实参

假如不想对应位置,特别是参数比较多的情况下,则可以实参调用时写上对应的参数名

def personal_message(name,gender):
    print('my name is '+name+', and my gender is '+gender)
    
personal_message(gender='male',name='wudl')

可以看到,顺序错了也没有问题

my name is wudl, and my gender is male

7.3 参数设置默认值–默认参数

假如我已经知道所有的人都是男性,那么我可以不需要每个调用都写male,而是在形参上直接写上默认值,如下

def personal_message(name,gender='male'):
    print('my name is '+name+', and my gender is '+gender)
    
personal_message('wudl')  #只需要写名字即可
my name is wudl, and my gender is male

但是上面需要记住,personal_message(name,gender='male')括号里面的参数不可以交换顺序,因为实参调用时,默认实参wudl占据第一个参数位置。
假如大量男性中出现了一个女性,那么上面的默认值也是有效的,此时只要重新将实参gender加上即可,如

def personal_message(name,gender='male'):
    print('my name is '+name+', and my gender is '+gender)
    
personal_message('lusy','female') 
#or
#personal_message('lusy',gender='female')
my name is lusy, and my gender is female

7.4 传递任意数量的实参*星号*args—建立空元组

7.4.1 只有*星号的形参

如果事先不知道要传递多少参数给函数,则可以使用带星号*的形参,星号意味着让python创建一个名为names的空元组(如下代码),常用*args表示,结果以元组的形式输出

def personal_message(*names):
    print(names)
    
personal_message('lusy')
personal_message('lusy','wudl','Daly')

7.4.2 同时有*星号和位置实参

同时存在位置实参,关键字实参和星号形参是比较常见的,也很实用,这个时候,通常建议将*号参数放在最后,则默认先匹配位置实参和关键字实参最后剩下的都收集到星号的形参中

def personal_message(gender,*names):
    print('gender: ',gender)
    print('names: ',names)
    
personal_message('male','wudl','Daly','Abdule')

def personal_message(gender,age,*names):
    print('gender: ',gender)
    print('names: ',names)
    print(age)
personal_message('male','18','wudl','Daly','Abdule')


注意:以下是错误写法,我将在下节讲解

def personal_message(gender,age,*names):
    print('gender: ',gender)
    print('names: ',names)
    print(age)
personal_message(gender='male',age='18','wudl','Daly','Abdule')

7.4.3 同时有*星号和位置实参

7.5 传递任意数量的键值对实参—两个星号**—建立空字典**kwargs

使用两个星号**让python建立一个名为user_info(如下代码)的空字典,通常我们使用**kwargs

def build_profile(first,last,**user_info):
    #创建空字典,有用户的所有信息
    profile = {}
    profile['first_name'] = first
    profile['last_name'] = last
    for key,value in user_info.items():
        profile[key] = value
    return(profile)

user_profile = build_profile('deliang','wu',
                             age='22',
                             location='Beijing')
print(user_profile)


>>>
{'age': '22', 'location': 'Beijing',
 'last_name': 'wu', 'first_name': 'deliang'}

我们输入的键值对age=‘22’,location='Beijing’会传递到由两个星号建立的空字典user_info中。

7.6 参数调用常见错误

错误1:SyntaxError: positional argument follows keyword argument

SyntaxError: positional argument follows keyword argument
这是常见的错误提示,内容为:位置参数跟随在关键字参数后面,意味着我们不能让位置参数出现在关键字参数后面。举例如下

def fun_(a,b):
    print(a,b)
fun_(a=1,3) #关键字参数a=1出现在了位置参数3前面


分析: 我们在调用fun_的时候使用了(a=1,3)即用了关键字参数a=1,而剩下的3我们企图用位置参数,但python解析的时候是无法这样用的。这是由于关键字参数可以不按照顺序出现,如fun_(b=1,a=3)这样的a,b倒置是合法的。因此一旦出现fun_(a=1,3)这样的写法,则开始操作时,python不知道a=1该放在a的位置还是b的位置。因此正确的用法是fun_(a=1,b=3)或者fun_(1,b=3),这种方式是1先定给了a,然后b=3再接着被分配.

def fun_(a,b):
    print(a,b)
fun_(3,b=2)  #将关键字参数放在最后是合法的

>>>
3 2
def fun_(a,b,c):
    print(a,b,c)
fun_(3,b=2,4)   #放在中间是不合法的,因为无法知道b和c的位置
def fun_(a,b,c):
    print(a,b,c)
fun_(3,2,c=4) #放在最后是合法的
错误2:non-default argument follows default argument

错误原因:“非默认参数跟随默认参数”。意味着我们不能让非默认参数跟随在默认参数后面,如下所示,意味着非默认参数b不能跟在默认参数a=1后面。这是因为python是按照顺序来的,3默认占了第一个位置即a=1的位置,但是a=1是默认参数,因此冲突,无法知道a=1还是3.

def fun_(a=1,b):
    print(a,b)
fun_(3)


下面是合法的

def fun_(a,b=1):  #3赋给第一个位置a,
    print(a,b)
fun_(3)

>>>
3  1

7.7 学会在参数中使用None

有时候我们想要设置一个参数,但是这个参数的值我们不知道,没有设定初始值,而又希望它存在,那么可以设定为None
1, 少参数

def fun_(a,b,c):
    print(a,b,c)
fun_(3,2) #少一个位置参数c


2,设定为None

def fun_(a,b,c=None):
    print(a,b,c)
fun_(3,2) 

>>>
3 2 None

3,改变None的初值

def fun_(a,b,c=None):
    print(a,b,c)
fun_(3,2,c=4) 

>>>
3 2 4

8类

8.0 类的调用

定义类的时候,一般对于python2而言,有括号,括号中放object,表示继承了object类,而python3默认继承object类,所以可以不放,可以参考python2和3类object区别

class Car(object): #python2
	xxx
class Car or Car():  #python3
	xxx

python3类中可以带括号也可以不带,但是调用的时候要有括号

c = Car()
c.xxx    #调用Car()下的方法xxx
or
Car().xxx  #要有括号

8.0* 函数调用


import numpy as np
  
def sin():
    return np.sin(3)

def sin_f(a=sin()):
    b = a*10
    return b

print(sin_f())

注意:上面的a=sin()是放在sin_f的括号里,如果放在函数下如:

def sin_f():
	a=sin()

会报错:a没有事先声明就调用

8.1修改类中属性的三种方法

1,直接修改属性的值
class User():
    def __init__(self, first_name, last_name):
        '''主方法,内涵first_name,last_name两个形参'''
        self.first_name = first_name
        self.last_name = last_name
        self.login_counter = 0  #登录次数,初始化为0

    def read_login_counter(self):
        print('The account has logged '+str(self.login_counter)+' times.')

user_a = User('dl','wu')
#user_a.login_counter = 3 #通过’句点表示法‘访问并修改属性的值
user_a.read_login_counter()

将上面注释去掉之前
The account has logged 0 times.
去掉之后
The account has logged 3 times.

2,通过方法改变属性的值

在类中定义的函数统称为方法,__init__()也是方法,我们可以新建一个更新参数的方法

class User():
    def __init__(self, first_name, last_name):
        '''主方法,内涵first_name,last_name两个形参'''
        self.first_name = first_name
        self.last_name = last_name
        self.login_counter = 0

    def read_login_counter(self):
        print('The account has logged '+str(self.login_counter)+' times.')
    
    def update_counter(self,num):
    '''更新输入次数的方法'''
        self.login_counter = num
        
user_a = User('dl','wu')
user_a.update_counter(3)
user_a.read_login_counter()

>>>
>The account has logged 3 times.
例题,禁止属性往回调

比如一辆汽车的里程是无法往回调的,只能往上调。这里假如输入次数也是如此,不能往下调。

class User():
    def __init__(self, first_name, last_name):
        '''主方法,内涵first_name,last_name两个形参'''
        self.first_name = first_name
        self.last_name = last_name
        self.login_counter = 5  #初始值设置为5
    
    def update_counter(self,num):
        '''禁止属性往下调'''
        if num > self.login_counter:    # 3小于5
            self.login_counter = num    
        else:
            print('禁止输入次数下调')
        
user_a = User('dl','wu')
user_a.update_counter(3)

>>>
>禁止输入次数下调
8.1.3 对属性的值递增

有时需要对属性值递增处理,定义一个函数

class User():
    def __init__(self, first_name, last_name):
        '''主方法,内涵first_name,last_name两个形参'''
        self.first_name = first_name
        self.last_name = last_name
        self.login_counter = 0

    def read_login_counter(self):
        print('The account has logged '+str(self.login_counter)+' times.')
    
    def update_counter(self,num):
        self.login_counter = num
        
    def increment_counter(self,num):
    '''递增属性值'''
        self.login_counter += num
        
user_a = User('dl','wu')
user_a.update_counter(3)
user_a.increment_counter(2)  #调用递增
user_a.read_login_counter()

>>>
>The account has logged 5 times.

8.2 类的继承和调用

类和属性的调用都采用句点表示法,即中间使用句号。
参考类的继承和调用

9 文件读取和异常try-except处理

9.1 文件读取和写入–open方法

参考文件读取

9.2 json读取和写入

在9.1节文件读取中有一定的涉及写入部分。写入有多个方法,下面说几个常用的方法
写入为json文件,使用json.dump写入和json.load()读取
1,json.dump写入

import json
name = 'wudl'

filename = 'user.json'
with open(filename,'w') as f_obj:
    json.dump(name, f_obj)

写入的文件有

"wudl"     #是str类型

2,json.load()读取

import json

filename = 'user.json'
with open(filename) as f_obj:
    username = json.load(f_obj)
    print(username)

读取一定要小心,这里的user.json文件中存放的是str类型,如“wudl”,如果存放的是wudl没有双引号,则会报错

9.3 异常

参考异常try-except

9.4 举例:1,输入用户名验证是否存在

加入你是游戏玩家,现在你输入用户名登录,假如你存在,则输出打招呼语言欢迎回来,如果不存在则注册并记住用户名。

import json

def get_stored_user():
    filename = 'user.json'
    try:
        with open(filename) as f_obj:
            username = json.load(f_obj)
    except FileNotFoundError:
        return(None)
    else:
        return(username)

def get_new_user():
    '''注册用户'''
    name = input('请输入新账号名: ')
    filename = 'user.json'
    with open(filename,'w') as f_obj: # w表示写入
        json.dump(name, f_obj)  #写入文件
    return(name)

def greet_user():
    '''查看用户是否存在,不存在则注册'''
    user_name = get_stored_user()
    if user_name:
        print(user_name+' ,欢迎回来!')
    else:
        response = input('没有此用户,是否注册?y/n')
        if response == 'y':
            user_name = get_new_user()
            print('我们已经记住您的用户名,欢迎进入游戏!')

greet_user()


如果出现如下错误,请看9.2节

10 os.path路径问题

提示:关于路径也可以参考之前笔者写的文章python路径

10.1 os.path.dirname和os.path.dirname(__file__)返回路径目录

只返回路径,不包括最后的文件(下面的test.py和test都被认为是文件)

接着我们看一下__file__是什么,

返回的是当前工作目录下的绝对路径,那么就可以知道os.path.dirname(__file__)表示去掉最后一个文件即components.py后的路径

10.2 os.path.join()路径拼接

这里.join()函数与os.path.join()是有区别的,前者可以参考前面第一部分的内容。
os.path.join()用于路径的拼接

11 input输入sys.stdin输入和print输出

1, input函数(菜鸟教程)
Input函数输出的是str类型,而且两个数直接没有逗号隔开


因为上面认为数字是连着的,所以输入的时候不能用逗号隔开,这样对于多输入的时候很不方便。但是可以使用如下的方法:
方法1,eval函数

实际上eval()函数只是将输入的字符串变为了元组,可以查看eval的用法

我们输入的是字符串,经过eval函数转变:

方法二: 使用.split()函数分开(常用),再通过map构成我们想要的类型,但需要注意的是,此时要用空格隔开


更新:python3中map已经变为了迭代器,不能直接得到值,可以如下:


所以如果是数字的话,可以加list来将之换成列表,如(注意map不能直接得到len)


2, sys.stdin
sys.stdin是一个标准化输入方法,可以认为sys.stdin在Python中是类似文件的对象 sys.stdin.readline()将所有输入视为字符串,并在最后包含换行符’\n’,可以通过sys.stdin.readline().strip(‘\n’)去掉换行符。
首先要先import sys

import sys
print('please input your name: ')
name = sys.stdin.readline()
print('Your name is: ',name)



可以看到sys.stdin是包括最后一个字符的,使用strip去掉

使用split分开

使用split()会自动去掉最后一个换行字符


多组输入

import sys
n = int(input()) #输入n组数
i = 0
while i < n:
    ans = map(float,sys.stdin.readline().split())
    print('answer = ',sum(ans))  #计算和
    i += 1

12 @符号–装饰器decorator和计时器time.time

@在python中用来表示装饰器,通常的用法有三种(在矩阵中表示矢量乘积)
可以参考bilibili上的一个视频,解释得很详细bilibili装饰器(强烈建议去看视频,作者介绍简单易懂)
以下简单讲解
1, 函数没有参数
我们现在来实现累计从[1,10)(不含10)的计算结果

import time 

def sum_n():
    t_start = time.time()
    sum_ = 0
    for i in range(10):
        sum_ = sum_ + i
    t_end = time.time()
    print(t_end - t_start)
    print(sum_)
    
sum_n()

从上面的式子看很可读性很差,因为计时是放在函数里面的,现在我们优化函数,使得计时器放在外面。

def display_time(func):   #decorator
    def wrapper():
        t_start = time.time()
        func()
        t_end = time.time()
        print(t_end - t_start)
    return wrapper

@display_time    # call decorator
def sum_n():
    sum_ = 0
    for i in range(10):
        sum_ = sum_ + i
    print(sum_)
    
sum_n()

上面新定义了一个函数叫做display_time用于显示时间,这个就是我们的装饰器,装饰器本质上是一个函数或类,概括的讲,装饰器的作用就是为已经存在的对象添加额外的功能。
display_time()括号里面的函数就是我们要调用的函数。
下面的wrapper就是我们要执行的内容,当我们调用sum_n()函数时(即最后一行)实质上没有执行sum_n而是先执行sum_n()上面一行的@display_time转到第一行函数,然后进入wrapper()里面。执行计算时间,然后func()(注意,这里func()就是我们的sum_n函数),再执行时间结束
结果如下

45
0.005215883255004883

2 函数带返回值

3 函数带有参数

当我们有参数时,只要在对应的wrapper和func里面放参数即可,上面展示了一种方法,但是使用*args更好,表示sum_n里面放多少参数则对应的一同放上去

换为可传递字典

13 矩阵矢量数组等乘法

  • 1 数组相乘对应点相乘,列表不能如此

  • 2 常数n与列表相乘表示列表重复n次,数组直接相乘


问题:如下,如何将a中的1和b的第一部分[4,5,6]相乘,而2和[7,8,9]相乘得到2x3的数组?
分析:显然不能直接相乘,因为数组是对应相乘,即14,25,6没有a的元素相乘,所以报错。思路:使用矩阵?矩阵不行,因为a变成矩阵是1x2,b是2x3,所以点乘是1x3。所以可以是用np.multiply
multiply表示的是元素相乘


其他更多的乘法见矩阵数组乘法; 矩阵和数组的区别

14 文件保存

参考笔者另一篇博文python保存文件
以后更新都在上面提到的博文上

15 time计时

见笔者另一篇博文time和timeit

16 全局变量和局部变量

python在函数可以使用全局变量也可以使用局部变量,python执行函数时会默认先寻找局部变量,如果局部变量没有这个变量名,则在全局变量中找,如果局部和全局都有这个变量名,则函数会默认使用局部变量,只有当局部没有这个变量,才会使用全局变量。因此,
1)对于全局变量是不可变类型而言(数字,字符串,元组,bool,None),在函数中可以直接调用但如果修改的话,那么会认为是重新定义了一个局部变量,所以没有修改全局变量的值

a = 4
b = 'name'
c = (1,2)

def func1():
    print(a)
    print(b)
    print(c)
直接使用全局变量
func1()

a = 4
b = 'name'
c = (1,2)

def func1():
对不可变类型而言,在局部变量中改变全局变量的值,
默认是重新定义一个新的变量,只是名字和全局变量相同罢了
所以实际上全局变量没有改变
    a = 1 
    b = 'dylan'
    print(a)
    print(b)

print(a)
print(b)
func1()
print(a)
print(b)


注意,如果在局部变量中调用了变量名,则会出错,这个时候可以声明是全局变量(见下)

a = 4
b = 'name'
c = (1,2)

def func1():
    a = a+1 #报错,没有事先声明a就使用a
    b = 'dylan'
    print(a)
    print(b)

2)如果要改变全局变量的值,可以在局部变量中事先申明变量是全局变量,使用关键字global

a = 4
b = 'name'
c = (1,2)

def func1():
    global a,b 声明是全局变量,则变量会改变
    a = 1
    b = 'dylan'
    print(a)
    print(b)

print(a)
print(b)
func1()
print(a)
print(b)

a = 4
b = 'name'
c = (1,2)

def func1():
    global a,b
    a = a+1  #声明是全局变量,则不会报错
    b = 'dylan'
    print(a)
    print(b)

print(a)
print(b)
func1()
print(a)
print(b)


3) 可变类型如果只是改变其中元素,则不需要申明global可以直接修改全局变量

import numpy as np

a = [1,2,3]
b = {'a':8,'b':9}
c = np.array([4,5,6])

def func1():
    a[0] = 'k'
    b['a'] = 'dylan'
    c[0]=20
    print(a)
    print(b)
    print(c)

print(a)
print(b)
print(c)
func1()
print(a)
print(b)
print(c)


4)可变类型如果是要修改整个变量,则同样需要声明global

import numpy as np

a = [1,2,3]
b = {'a':8,'b':9}
c = np.array([4,5,6])

def func1():
这里的局部变量a,b,c不是全局变量,只是名字相同罢了
不会影响全局变量的值
    a = [3,2,1]
    b = 'dylan'
    c = np.array([10,11,12])
    print(a)
    print(b)
    print(c)

print(a)
print(b)
print(c)
func1()
print(a)
print(b)
print(c)


5)常见错误

num = 3

def printnum():
    print(num)
    num = 4
    print(num)

printnum()
print(num)

这里会报错,局部变量在声明之前就使用了。因为python优先在函数中找局部变量,发现找到了num=4这个变量,但在这个变量之前却先print(num),所以出现错误

16python是动态语言

这里参考视频:python动态语言的三种表现形式
定义见下面的图

表现形式有三种,建议看上面的视频

17 使用latex标注

17.1 plt中的latex

在latex中,使用符号如 α \alpha α的方法是

'$\\theta$'  #转义
or
r'$\\theta$'

原因是:在python中\是被占用的,需要使用\来转义,转义的方法有两种,一是用\,二是使用r。

import matplotlib.pyplot as plt
import numpy as np

x = np.linspace(-10,10,100)
y = np.sin(x)

plt.plot(x,y)
plt.xlabel('$\\theta$')
plt.ylabel('$\\beta$')
plt.title('$\\beta=sin(\\theta)$')

plt.show()

17.2 getdist中的latex

尝试画triangle plot的时候出现了问题

ValueError: 
\\beta=sin(\\theta)
^
Unknown symbol: \ (at char 0), (line:1, col:1)

getdist比较奇葩,它不按照一般套路出牌,上面的两种方法都不对,需要使用下面的方法:

'\\theta'
or
r'\theta'

实际上跟上面是一样的,因为latex必须在$下输入,而这里只不过在函数定义的时候已经给我们加了$ :见getdist

labels = ['r','A_s','\\alpha_s','\\beta_s','A_d','\\alpha_d','\\beta_d']使用\\
samples = MCSamples(samples=samps,names = names, labels = labels,ranges={'r':(0,None),
                                                                         'As':(0,None)})

18, 压缩解压文件

18.1 常规方法:

1)使用tarfile

官网:tarfile
官网很全,这里不一一翻译,只给几个常用的方法:
解压tar文件:

import os, tarfile

def unpack_path_file(pathname):
    archive = tarfile.open(pathname,'r:gz')  只读方式打开gzip压缩的文件
    for tarinfo in archive:
        archive.extract(tarinfo,os.getcwd())
    archive.close()
    
unpack_path_file('00.tar.gz') 实例

解释:

  • archive.extract用法为TarFile.extract(member, path="", set_attrs=True, *, numeric_owner=False);其中member是指解压的文件,path指要解压到的文件夹路径,如果没有,则默认到当前工作目录
  • os.getcwd()获取当前工作目录,由于默认到前面工作目录,所以可以不写
    除上面的解压文件功能外,还有如下常用功能。

判断是否是压缩文件

查看压缩文件下有多少文件

表示在00这个压缩文件下还有01.tar.gz, 02.tar.gz等等
获取压缩文件下的文件名

显示压缩文件下的文件信息(如果verbose=False则类似linux的ls -l命令)

解压所有文件到指定路径(我这里指定的是当前工作目录下的ext目录)

添加文件到压缩包中
需要用可写方式打开压缩包(添加lecture_all.pdf到压缩文件中)

其他用法

例题可以参考官网这里不述说了。
tarfile在命令行中使用
通过在终端输入:

python -m tarfile --help

来查看帮助文件

python -m tarfile -c newtest.tar ext test.py  将ext文件夹和test.py压缩到newtest.tar中
上面的newtest.tar也可以改为任何压缩形式,如newtest.tar.gz
python -m tarfile -e monty.tar  other-dir/ 将文件提取到指定路径
python -m tarfile -l monty.tar  显示tar文件中的文件
2)使用zipfile

18.2 使用patool

参考文献:https://github/wummel/patool,https://wummel.github.io/patool/,https://pypi/project/patool/

19, python其它常见函数

19.1 isinstance()函数

参考菜鸟教程

用法

isinstance(object,calssinfo)
  • object:表示输入的实例对象
  • classinfo:基本类型,如int,float,bool,complex,str(字符串),list,dict(字典),set,tuple等,用于判断object输入的是否和此类型一致;一致则返回True,否则False

举例:

19.2 assert函数

参考菜鸟教程

20 matplotlib画图

20.1 subplot子图

参考笔者另一博文

21 插值

21.1 numpy线性插值numpy.interp()

这部分参考官网,也可以看这篇博文,这两篇就差不多懂了。

21.2 scipy插值, scipy.interpolate()

21.2.1一维插值

参考python科学计算(第二版)3.7节。
举书上的例子

from scipy import interpolate
import numpy as np
import matplotlib.pyplot as plt 


x = np.linspace(0,10,11)
y = np.sin(x)
plt.plot(x, y, '-ro')

# xnew = [0.2,1.8,3.5,6.8]
xnew = np.linspace(0,10,101)
for kind in ['nearest','zero','slinear','quadratic']:
    f = interpolate.interp1d(x,y,kind=kind)
    ynew = f(xnew)
    plt.plot(xnew,ynew,label=str(kind))

plt.legend(loc='lower right')


上面的zero或者nearnest都是0阶B样插值,linear或者slinear是一阶B样插值,quadratic和cubic分别是二阶和三阶B样条曲线。
插值的一个特点(与拟合的区别)是曲线一定会经过原始数据点。对于数据很乱,没有规律显然使用插值比拟合更好。
现在数据不是sin函数,而是随意的值,如下,其他不变(加了‘cubic’),继续使用上面的插值方法

y = [0, 2, 8, 3, 9, 5, 6, 7, 10, 2, 3, 8]
x = np.arange(len(y))
from scipy import interpolate
import numpy as np
import matplotlib.pyplot as plt

y = [0, 2, 8, 3, 9, 5, 6, 7, 10, 2, 3, 8]
x = np.arange(len(y))
plt.plot(x, y, '-ro')

# xnew = [0.2,1.8,3.5,6.8]
xnew = np.linspace(0, 10, 101)
for kind in ['nearest', 'zero', 'slinear', 'quadratic','cubic']:
    f = interpolate.interp1d(x, y, kind=kind)
    ynew = f(xnew)
    plt.plot(xnew, ynew, label=str(kind))

plt.legend(loc='lower right')


**超出边界:**如果插入的值xnew超出边界,则会报错。比如:
正常情况:插入的值xnew = 1.5,在(0,10)之间

import matplotlib.pyplot as plt
from scipy import interpolate
import numpy as np

x = np.arange(0,10)
y = np.exp(-x/3.0)
f = interpolate.interp1d(x, y)

xnew = 1.5
ynew = f(xnew)
plt.plot(x,y,'-ro',xnew,ynew,'ko')
plt.show()


如果插值的值xnew = -1, 小于x[0],则报错:

为了解决这种情况,可以有两种办法:请查看官网解释

方法1: 给边界定值,当xnew<x[0]或者xnew>x[-1]时,给左右两边的边界定值。
为此,我只需要将bounds_error=False,并将fill_value设定为边界值。
比如我定义,当xnew<x[0]时,y=1;当xnew>x[-1]时,y=0.05,则程序如下:

import matplotlib.pyplot as plt
from scipy import interpolate
import numpy as np

x = np.arange(0,10)
y = np.exp(-x/3.0)
f = interpolate.interp1d(x, y,bounds_error=False, fill_value=(1,0.05))

xnew = [-2,-1,12,15]
ynew = f(xnew)
plt.plot(x,y,'-ro',xnew,ynew,'ko')
plt.show()


方法2:对结果外推
上面的方法1适合在我们知道边界值的情况,而如果我们想将边界外推,则只需要将fill_value='extrapolate'即可

import matplotlib.pyplot as plt
from scipy import interpolate
import numpy as np

x = np.arange(0,10)
y = np.exp(-x/3.0)
f = interpolate.interp1d(x, y,bounds_error=False, fill_value='extrapolate')

xnew = [-2,-1,12,15]
ynew = f(xnew)
plt.plot(x,y,'-ro',xnew,ynew,'ko')
plt.show()

21.2.2 二维插值interp2函数–平面上所有点

参考官网,所谓二维,指的是一个二维平面,平面上铺满了点(值)而不是一条线,所以首先要有meshgrid网格。举例:

**解释:**上面的图红色线和点是原本的值(只画了x维度的)蓝色线是插值的结果。上面一开始是建立了一个二维网格xx,yy,原因是为了计算出二维网格上的值z,这样也就是平面上的每个点都有一个值z。在插值的时候是不需要xx,yy的,只需要一维的x,y即可,但是传入的是二维的z。如果事先已经有了二维平面上每个点的值z,那么就不需要做网格了(请看下面的数据插值部分)。在构造了插值函数,也就是上面的f之后(再次提醒:上面f的右边,x,y是一维的,z是二维的),现在需要对想要的点插值,上面的例子传入的是xnew,ynew,都是一维的,出来的结果是二维的,比如x是从1-10,y也是1-10,那么出来的znev是 10 × 10 10\times 10 10×10的二维平面,也就是平面上每个点都有值。

数据插值
现在来解释,如果有了平面的数据之后,如何插值。(这里就不用再建立二维网格xx,yy了)
比如我们有了二维10 by 10的数据,横纵坐标分别是经纬度。

(上面data第一列表示lon=79,lat从31.25到33.5(从小到大,不是从33.5到31.25)对应的值)

现在任意给几个经纬度的点,要求插值出给的点的所有值。

由于data是二维的数据,所以就不用再弄出二维网格了。
上面我给的点刚好是在原data文件中有值的,从上面的分析,我们知道,结果肯定是:

  • 1) 第一列,当lon_new=79,lat_new=32.75(从小到大的顺序)到33.5时的值
  • 2)第二列,也是lon_new=79,lat_new=32.75到33.5的值
  • 3) 上面的结果是在data的列中的。

griddata
但是,如果我们并不想要一个平面上的所有点都插值,而是我们想知道给定对应的x和y上的点时,求出对应点的插值。这个时候我们需要用的是**griddata函数**(当然也可以直接从上面二维网格插值的结果中提取需要的点的值即可,比如第一列就是表示的对应坐标的值)

21.2.3 二维插值griddata函数—插值对应的(x,y)坐标的点

以这篇博文的数据为例

已知坐标点(x,y)及对应的z的值,现在给定任意坐标点(x,y),求插值的z的值。

from scipy.interpolate import griddata
import numpy as np
import matplotlib.pyplot as plt

points = np.array([[129, 7.5], [140, 141.5], [103.5, 23], [88, 147], [185.5, 25.5], [195, 137.5], [
                  105, 85.5], [157, -6.5], [107.5, 81], [77, 3], [81, 56.5], [162, -66.5], [162, 84], [117.5, -33.5]])
values = np.array([4, 8, 6, 8, 6, 8, 8, 9, 9, 8, 8, 9, 4, 9])  # 已知散点的值
xi, yi = [129, 185.5, 88], [7.5, 25.5, 147] #新坐标
znew = griddata(points, values, (xi, yi), method='cubic')  # 进行插值
print(znew)


解释:上面我需要插值的点是xi, yi = [129, 185.5, 88], [7.5, 25.5, 147],这个点实际上在表3中可以找到,这里相当于检验是否结果正确。可以看到输出的z跟表3是一致的。

21.2.4 例题

如果已经有了二维平面的值,如何插值出对应点(x,y)的值。比如上面已经有了经纬度对应的平面数据data,现在要求插值出(lon,lat)=(79,33.5),(80,33)这两个点的值。
(从数据data上已经读取这两个点的值分别是:259.17928325,259.52985878)

  • 1)一个一个值插值。这样速度比较慢,如果点很多的话需要for循环。点不多的话是可以接受的方法,可以事先定义f=interp2d(lon,lat,data,kind=‘cubic’)循环的时候放的是f,这样避免重复打包数据到interp2d中

  • 2)输入多个值,但是需要考虑结果的顺序(因为结果都是x,y从小到大的,联想我们的直角坐标系),下面可以看到,不论我怎么改变y的顺序,结果都不变。即第一列表示的是x=79,y=33和33.5的值,是从小到大的(也就是下面的a11,a12)。我们要的是A,B两点。

  • 3)另外一个思路是先interp2d插值出结果,然后使用griddata一次性查所有点,但这个稍微繁琐点,如果不考虑时间的话1)是最好的方法。

21.3 matlab进行插值计算

matlab和mathematica在图片展示方面远远比python强,也比python方便,尤其是高维图,可以转动查看。点击上面的链接即可转到笔者关于matlab插值的博文。

22 积分

参考scipy.integration

22.1一重积分

参考官网一重积分


计算 ∫ 0 2 x 2   d x \int_{0}^{2}x^2 \, dx 02x2dx

import numpy as np 
from time import ctime
from scipy.integrate import tplquad, dblquad, quad

#计算一重积分,使用scipy自带的函数
print('start quad at: ', ctime())
val2, err2 = quad(lambda x: x**2,0,2) #err2是误差,使用了匿名函数,x的积分区间是0到2
print(val2)
print('end quad at: ', ctime())

#自定义一重积分
def myquad(startx,endx,stepx):
    t = 0
    for x in np.arange(startx,endx,stepx):
        t = t + x**2*stepx
    return t

print('start myquad at: ', ctime())
print(myquad(0,2,0.001))
print('end myquad at: ', ctime())


更一般性的,使用下面的方法:

#计算一重积分,使用scipy自带的函数
def fun():
    return lambda x:x**2

print('start quad at: ', ctime())
val2, err2 = quad(fun(),0,2)
print(val2)
print('end quad at: ', ctime())

间隔stepx越小精度越高;另外当精度一样时,通过调用函数计算更快

22.2 二重积分

使用dblquad函数,第一个变量x的区间是a到b,第二个变量的区间可以是一个关于x的函数,通过x计算得到y的区间,这样可以对x-y平面上的任何区间进行二重积分。


计算 ∬ 0 2 x y + x 2 + y 2   d x   d y \iint_{0}^{2}\sqrt{xy+x^2+y^2} \, dx\,dy 02xy+x2+y2 dxdy

import numpy as np 
from time import ctime
from scipy.integrate import tplquad, dblquad, quad
#使用scipy带的积分函数
#第一个0,2表示x的积分上下限,第二个0,2表示y的上下限
#这里使用了匿名函数lambda
def fun2():
    return lambda y, x: (x*y+x**2+y**2)**(1/2)
    
print('start dblquad at: ',ctime())
val2,err2 = dblquad(fun2(),0,2,0,2)
print(val2)
print('end dblquad at: ',ctime())

# 通过循环自己计算积分
print('start mydblquad at: ', ctime())
def mydblquad(start,end,stepx,stepy):
    t = 0
    for x in np.arange(start,end,stepx):
        for y in np.arange(start,end,stepy):
            t = t + (x*y+x**2+y**2)**(1/2)*stepx*stepy
    return t

print(mydblquad(0,2,0.001,0.001))
print('end mydblquad at: ', ctime())


从上面显然可以看到调用函数的优势,计算二重积分0秒,而循环要17秒。
注意:dblquad中y是第一个参数,x才是第二个参数:

举个简单例子:求积分,x从0到2,y从0到1
∫ 0 2 ∫ 0 1 x y 2 d x d y \int_0^2\int_0^1xy^2dxdy 0201xy2dxdy
上面手动解析积分得到:
x 2 2 ∣ 0 1 × 1 3 y 3 ∣ 0 2 ≃ 1.333 \frac{x^2}{2}\bigg|_0^1\times\frac{1}{3}y^3\bigg|_0^2\simeq1.333 2x201×31y3021.333
在使用dblquad()时,按照上图关于dblquad的定义,要求表达式func的定义为:fun(y,x),即y是第一个参数,x是第二个参数,这样在dblquad中a,b才表示x的范围,gfun和hfun才表示y的范围。

例2:存在第三个变量,但这个变量在for循环中,如:
C ℓ = ∫ 0 2 ∫ 0 1 x y ( ℓ + x ) 2 d x d y C_\ell = \int_0^2\int_0^1xy(\ell+x)^2dxdy C=0201xy(+x)2dxdy
x的积分从0到1,y的积分从0到2,最后得到的是关于 ℓ \ell 的函数

源代码

from scipy.integrate import dblquad

def test(y,x):
	# global l
    w = (l+x)**2
    return x*y**2*w 

k = []
for l in range(2,10):
    val, err = dblquad(test, 0, 1, 0, 2)
    k.append(val)
print(k)

例3:积分存在分母为零的情况:为了简单,将上面的函数改为
C ℓ = ∫ 0 2 ∫ − 2 1 y 2 ℓ + x d x d y C_\ell = \int_0^2\int_{-2}^1\frac{y^2}{\ell+x}dxdy C=0221+xy2dxdy
显然对 ℓ \ell 循环的时候,当 ℓ = 1 , x = − 1 ; ℓ = 2 , x = − 2 \ell=1,x=-1;\ell=2,x=-2 =1x=1;=2,x=2都可以使得分母为0;这种情况是不能避免的,通常也不会有这样使得分母为0的函数,比如可能 ℓ \ell 直接从3开始循环,或者定义当分母 ℓ + x = 0 \ell+x=0 +x=0时,令 ℓ + x = 1 \ell+x=1 +x=1;我们采用后者:

from scipy.integrate import dblquad

def test(y,x):
    w = l+x
    if w == 0:    #如果分母=0,直接令分母为1
        w = 1   
    return y**2/w 

k = []
for l in range(1,10):
    val, err = dblquad(test, -2, 1, 0, 2)
    k.append(val)
print(k)

22.3 多重积分

参考官网,里面的tplquad, nquad,这里仅仅给一例加以说明
计算
∫ 0 1 ∫ 2 4 ∫ − 1 5 ∫ − 2 3 ( x 0 + x 1 + 2 x 2 + 3 x 3 ) d x 0 d x 1 d x 2 d x 3 \int_0^1\int_2^4\int_{-1}^5\int_{-2}^3(x_0+x_1+2x_2+3x_3)dx_0dx_1dx_2dx_3 01241523(x0+x1+2x2+3x3)dx0dx1dx2dx3
这里的范围分别为 x 0 ∈ [ 0 , 1 ] , x 1 ∈ [ 2 , 4 ] a n d s o o n x_0\in[0,1],x_1\in[2,4] and so on x0[0,1],x1[2,4]andsoon,则写程序的时候一定要注意顺序

def func():
    return lambda x0,x1,x2,x3: x0+x1+2*x2+3*x3

print(nquad(func(),[[0,1],[2,4],[-1,5],[-2,3]])) #注意顺序


(参数不一定要以 x 0 x_0 x0等命名,可以是其它字符,如 x , y , z , w x,y,z,w x,y,z,w等)

22.4 被积函数含有其它变量

这在积分中很常见,可以参考官网,把它放到args参数中,这个时候不对args中的变量积分。如下例子,不会对a,b积分,所以只有一个变量 x x x,因此默认对 x x x积分,不用匿名函数lambda

以及这个例子


具体可以点上面给的链接

22.5 不使用匿名函数求积分

除了22.4节之外,其他节求积分都是通过关键字lambda将被积函数变为匿名函数;所以受22.4节启发,可以将被积函数的被积变量作为参数输入。现在重新计算前几节

22.5.1 一重积分

计算
∫ 0 2 x 2 d x \int_0^2x^2dx 02x2dx

import numpy as np 
from time import ctime
from scipy.integrate import tplquad, dblquad, quad

#定义被积函数
def integrand(x):       
    return x**2
#计算一重积分,使用scipy自带的函数
print('start quad at: ', ctime())
val2, err2 = quad(integrand,0,2) #err2是误差,使用了匿名函数,x的积分区间是0到2
print(val2)
print('end quad at: ', ctime())

22.5.2 二重积分

计算 ∬ 0 2 x y + x 2 + y 2   d x   d y \iint_{0}^{2}\sqrt{xy+x^2+y^2} \, dx\,dy 02xy+x2+y2 dxdy

import numpy as np 
from time import ctime
from scipy.integrate import tplquad, dblquad, quad
#使用scipy带的积分函数
#第一个0,2表示x的积分上下限,第二个0,2表示y的上下限
#这里使用了匿名函数lambda
def fun2(x,y):
    return (x*y+x**2+y**2)**(1/2)
    
print('start dblquad at: ',ctime())
val2,err2 = dblquad(fun2,0,2,0,2)
print(val2)
print('end dblquad at: ',ctime())
22.5.3 被积函数含有多个函数

例1,计算
∫ 0 2 ∫ − 2 2 ( x y 2 ) d x d y \int_0^2\int_{-2}^2(xy^2)dxdy 0222(xy2)dxdy
这里x的范围是[-2,2],y的范围是[0,2],按照二重积分的定义,[a,b]是x的范围,所以写被积函数integrand(y,x)要先y再x

import numpy as np 
from time import ctime
from scipy.integrate import tplquad, dblquad, quad

def func1(x):
    return x

def func2(y):
    return y**2

def integrand(y,x):   #注意,这里是先y再x,
    return func1(x)*func2(y)
    
print('start dblquad at: ',ctime())
val2,err2 = dblquad(integrand,-1,2,0,2)  #x的范围是[-1,2],y是[0,2]
print(val2)
print('end dblquad at: ',ctime())


例2,计算
∫ 1 2 ∫ − 1 4 r ∗ r ′ d x d y \int_1^2\int_{-1}^4r*r'dxdy 1214rrdxdy
其中 r r r是向量, ∗ * 表示点乘,分别如下:

  • r = ( x , y ) r=(x,y) r=(x,y)
  • r ′ = ( y , 1 ) r'=(y,1) r=(y,1)

所以上面的结果其实是
∫ 1 2 ∫ − 1 4 ( x y + y ) d x d y \int_1^2\int_{-1}^4(xy+y)dxdy 1214(xy+y)dxdy

import numpy as np 
from time import ctime
from scipy.integrate import tplquad, dblquad, quad

def fun1(r):
    return r

def fun2(r_prime):
    return r_prime

def integrand(y,x):
    return np.dot(fun1(np.array([x,y])),fun2(np.array([y,1])))
    
print('start dblquad at: ',ctime())
val2,err2 = dblquad(integrand,-1,4,1,2)
print(val2)
print('end dblquad at: ',ctime())

23 any()和all()

这一部分写在此博客

24 input和sys.stdin输入

25 format格式化函数

具体介绍可以参考format函数

25.1 科学计数法输出


第二部分:python应用项目

1 Web应用程序—Django入门

我是在windows进行这个项目的操作的,

1.1 创建项目

建立虚拟环境

python -m venv 11_env

激活虚拟环境
windows下

11_env\Scripts\activate

linux下

source 11_env/bin/activate

安装Django

pip install Django

直接用pip而不是python -m pip install是因为此时的pip已经是虚拟环境中的pip了,可以pip -V查看发现pip是按装在虚拟环境中的。
在Django中创建项目
linux

django-admin.py startproject learning_log .

注意最后有一个点。将会建立一个新文件夹名为learning_log
windows
如果使用上面的方法创建失败,则使用下面命令

python xxxx\django\bin\django-admin.py startproject learning_log

xxx表示django-admin.py的存放路径
创建数据库

python manage.py migrate

如果manage.py报错说不存在,那么可能你多建了一个learning_log的文件夹,从里面剪切过来即可,到目前为止,我们的文件如下:learning_log文件夹下有11_env, learning_log, manage.py和运行上面命令新增加的db.sqlite3

2 python生成动态视频

此部分参考公众号量化投资与机器学习
效果是下面的动态形式

源代码如下:公众号代码有小错误,下面代码是修改之后的

import pandas as pd
import matplotlib.pyplot as plt
import matplotlib.ticker as ticker 
import matplotlib.animation as animation
from IPython.display import HTML

df = pd.read_csv('https://gist.githubusercontent/johnburnmurdoch/4199dbe55095c3e13de8d5b2e5e5307a/raw/fa018b25c24b7b5f47fd0568937ff6c04e384786/city_populations', 
                 usecols=['name', 'group', 'year', 'value'])

fig, ax = plt.subplots(figsize=(15, 8))
colors = dict(zip(
    ['India', 'Europe', 'Asia', 'Latin America',
     'Middle East', 'North America', 'Africa'],
    ['#adb0ff', '#ffb3ff', '#90d595', '#e48381',
     '#aafbff', '#f7bb5f', '#eafb50']
))
group_lk = df.set_index('name')['group'].to_dict()
def draw_barchart(year):
    dff = df[df['year'].eq(year)].sort_values(by='value', ascending=True).tail(10)
    ax.clear()
    ax.barh(dff['name'], dff['value'], color=[colors[group_lk[x]] for x in dff['name']])
    dx = dff['value'].max() / 200
    for i, (value, name) in enumerate(zip(dff['value'], dff['name'])):
        ax.text(value-dx, i,     name,           size=14, weight=600, ha='right', va='bottom')
        ax.text(value-dx, i-.25, group_lk[name], size=10, color='#444444', ha='right', va='baseline')
#        ax.text(value+dx, i, dff'{value:,.0f}',  size=14, ha='left',  va='center')
        ax.text(value+dx, i,'%.0f'%value,  size=14, ha='left',  va='center')
    ax.text(1, 0.4, year, transform=ax.transAxes, color='#777777', size=46, ha='right', weight=800)
    ax.text(0, 1.06, 'Population (thousands)', transform=ax.transAxes, size=12, color='#777777')
    ax.xaxis.set_major_formatter(ticker.StrMethodFormatter('{x:,.0f}'))
    ax.xaxis.set_ticks_position('top')
    ax.tick_params(axis='x', colors='#777777', labelsize=12)
    ax.set_yticks([])
    ax.margins(0, 0.01)
    ax.grid(which='major', axis='x', linestyle='-')
    ax.set_axisbelow(True)
    ax.text(0, 1.12, 'The most populous cities in the world from 1500 to 2018',
            transform=ax.transAxes, size=24, weight=600, ha='left')
#     ax.text(1, 0, 'by QIML', transform=ax.transAxes, ha='right',
#             color='#777777', bbox=dict(facecolor='white', alpha=0.8, edgecolor='white'))
    plt.box(False)
    
draw_barchart(2018)
animator = animation.FuncAnimation(fig,draw_barchart, frames=range(1960,2019))
HTML(animator.to_jshtml())

3 将新数组插入原始数组

如下,有原始数组(x,y),即每个x对应一个y,x的范围是(0,10)间隔为1,y是sin(x)。现在有两个新的点,即[1.5,1.8],且它们的值都是1,如何将这两个新点放入原始数组(x,y)中?要求x是递增,即1.5要在1,2之间。

import numpy as np 
import matplotlib.pylab as plt

x = np.arange(0,10)
y = np.sin(x)

xnew = [1.5,2.8]
ynew = [1,1]

plt.plot(x,y,'-')
plt.show()


我们可以通过将(x,y)生成字典对来解决这个问题,可以参考字典那一节。

import numpy as np 
import matplotlib.pylab as plt

x = np.arange(0,10)
y = np.sin(x)

xnew = [1.5,2.8]
ynew = [1,1]

plt.plot(x,y,'-')
plt.show()

x_merg = list(x)+list(xnew)  #将x和xnew组合
y_merg = list(y)+list(ynew)   #将y和ynew组合

x_y_dic = dict(zip(x_merg,y_merg))  #构成字典
print(x_y_dic)
print(x_y_dic[1.5])  #输出x=1.5对应的y
print(x_y_dic[2.8])   #输出x=2.8对应的y

第三部分:python常见错误

1函数调用之后原始值变化

1,

2,cmb_map() missing 1 required positional argument: ‘self’

函数调用之后报错说某某函数缺少一个必要的位置变量。
如在a.py中写了一个函数

class CMB():
    def __init__(self):
        pass
    
    def BB(self,nu,T):
        return pysm.common.B(nu,T)
    
    def cmb_map(self,config=None):
        cmb_Map = hp.read_map(r'../data/psm/components/cmb/cmb_map.fits',
                       field=(0,1,2))
        cmb_I, cmb_Q, cmb_U = cmb_Map[:]
        return cmb_I, cmb_Q, cmb_U

现在在b.py中

from a import CMB
output = CMB.cmb_map()

报错

这是因为我们 使用的是类,我们在类中如果写了self参数,在调用类的时候一定要打括号
改正如下:

from a import CMB
output = CMB().cmb_map() #加括号

3 Unable to allocate array with shape (5, 432, 360, 3000) and data type float64内存不够

有时候数据量太大,导入之后内存不够,比如有a,b,c三个文件,都是(5,432,360,3000)维度,每个文件16G大小,导入之后想执行a+b+c的简单操作,却说Unable to allocate array with shape (5, 432, 360, 3000) and data type float64,这个时候一方面可以适当降低数据精度,比如从float64降低到float32,那么每个文件只有8G。另一方面,可以通过for循环来覆盖,而不是直接导入之后执行a+b+c,

for i in range(5):
	a[i] = a[i]+b[i]+c[i]  #求和之后的结果覆盖a参数,不用重新分配内存

如果上面还不能分配,可以再来一个循环

for i in range(5):
	for j in range(432):
		a[i,j] = a[i,j] +b[i,j]+c[i,j] 

如果上面都不行,那么考虑将数据a,b,c都拆分成小文件来保存,然后每次导入内存的都是小文件

第四部分:python巧妙应用

1 找出列表/数组/元组中某个字符的index及其拓展

1.1 返回某个字符的index


1.2 拓展:找出某个列表/元组中的某个子元组

如val的值如下,是一个三元组

val = ((1,2,3),({'name':'dylan'},('language','english')),('nside',1024))

假如第二个维度即({‘name’:‘dylan’},(‘language’,‘english’))是很长的一个组分,即

({'name':'dylan'},...('Alan',32)...,('language','english'))

由于组分太长,没必要打开,或者打开很久,但是我知道里面肯定有(‘Alan’,32)这个元素;而我要在不打开的情况下找到Alan年龄多大(32岁)。
为了简单起见,我们只考虑里面没有其他多余的成分,即val如下
1, 首先查看到val是三维数组

2,假如我们已知Alan是在第二组中,找出Alan(用列表解析)

以下可以不看
说这个的原因是在map处理中导入的时候头文件是有NSIDE的,我们忘记NSIDE的时候可以找到。

NSIDE就在后面的元组中,

2,通过split设置输出文件名

这部分是笔者笔记,可跳过
问题描述:现有文件数据,放在文件夹中,有两种类型,small和large,这两种类型下又分别含有名为95_150mat.txt, 41_95_150mat.txt等文件,现在要求输入的文件名对应为95_150_small.pdf, 95_150_large.pdf等。比如有如下文件:

data_path = r'..\..\data\large\results\three\r_0.01\95_150_220mat.txt'

则输出的文件名应该对应为95_150_220_large.pdf
实际上,通过参考第一部分提到的split函数,基本已经可以完成这个任务。
通过下面的方法即可找到对应的字符串

保存的时候如下:

bands_str+'_'+fsky_scale+'.pdf'

完整的python code

from __future__ import print_function
import sys, os
sys.path.insert(0,os.path.realpath(os.path.join(os.getcwd(),'..')))
from getdist import plots, MCSamples
import getdist, IPython
import pylab as plt
print('GetDist Version: %s, Matplotlib version: %s'%(getdist.__version__, plt.matplotlib.__version__))
import numpy as np

def get_diagonal_element(matrix):
    # get diagonal elements of a given matrix
    mat = matrix
    for i in range(np.shape(mat)[0]):
        mat[i,i] = mat[i,i]**2
    return mat

nsamp = 10000
np.random.seed(10)
data_path = r'..\..\data\large\results\three\r_0.01\95_150_220mat.txt'
data = np.loadtxt(data_path)
#data = np.loadtxt(r'C:\Users\Lenovo\Desktop\recent work\revise paper\data\small\results\two\r_0.01\95_150cov.txt')
data = get_diagonal_element(data)
cov = data
ndim = data.shape[0]
if 'large' in data_path:
    mean_ = [0.01,0.91,-2.6,-3.14,315*0.53,-2.54,1.53] # large sky with fsky = 0.71
    print('fsky:>>>> large')
else:
    mean_ = [0.01,0.081,-2.6,-3.14,20*0.53,-2.54,1.53] # small sky with fsky = 0.05
    print('fsky:>>> small')
samps = np.random.multivariate_normal(mean_, cov, size=nsamp)

names = ['r','As','alpha_s','beta_s','Ad','alpha_d','beta_d']
labels = ['r','A_s',r'\alpha_s','\\beta_s','A_d','\\alpha_d','\\beta_d']
samples = MCSamples(samples=samps,names = names, labels = labels,ranges={'r':(0,None),
                                                                         'As':(0,None),
                                                                         'Ad':(0,None)})

g = plots.getSubplotPlotter()
samples.updateSettings({'contours':[0.68,0.95,0.99]})
g.settings.num_plot_contours = 3
g.triangle_plot(samples,filled=True)
#g.triangle_plot([samples, samples2], filled=True)
bands_str = data_path.split('\\')[-1].split('m')[0]
fsky_scale = data_path.split('\\')[3]
g.export(bands_str+'_'+fsky_scale+'.pdf')

第五部分:python例题

1, 变位词判断

1,逐字对比

import time

def display_time(func):
    def wrapper(*args):
        t1 = time.time()
        func(*args)
        t2 = time.time()
        print('time cost: %.6f'%(t2-t1))
    return wrapper

@display_time
def anagram_solution(word1,word2):
    w1_list = list(word1)
    w2_list = list(word2)
    if len(word1) != len(word2):
        print(word1+' and '+word2+' are not anagram')
    else:
        for i in range(len(word1)):
            stillOK = True
            for j in range(len(word2)):
                if w1_list[i] == w2_list[j]:
                    w2_list[j] = None
            if stillOK == False:
                print(word1+' and '+word2+' are not anagram')
        if stillOK == True:
            print(word1+' and '+word2+' are anagram')
        print(w2_list)
                    
                
a = 'typhon'
b = 'python'
anagram_solution('python','typhon')

2, 先排序,再对比

import time

def display_time(func):
    def wrapper(*args):
        t1 = time.time()
        func(*args)
        t2 = time.time()
        print('time cost: %.6f'%(t2-t1))
    return wrapper

@display_time
def anagram_solution(word1,word2):
    if len(word1) != len(word2):
        print(word1+' and '+word2+' are not anagram')
    else:
        w1_list = list(word1)
        w1_list.sort()
        w2_list = list(word2)
        w2_list.sort()
        stillOK = True
        for i in range(len(w1_list)):
            if w1_list[i] != w2_list[i]:
                stillOK = False
                print(word1+' and '+word2+' are not anagram')
                break
        if stillOK == True:
            print(word1+' and '+word2+' are anagram')
                    
                
a = 'typhon'
b = 'python'
anagram_solution('python','typhon')

3 通过计数来比较

import time
#import numpy as np

def display_time(func):
    def wrapper(*args):
        t1 = time.time()
        func(*args)
        t2 = time.time()
        print('time cost: %.6f'%(t2-t1))
    return wrapper

@display_time
def anagram_solution(word1,word2):
    if len(word1) != len(word2):
        print(word1+' and '+word2+' are not anagram')
    else:
        w1_ = [0]*26
        w2_ = [0]*26
        w1_list = list(word1)
        w2_list = list(word2)
        for i in range(len(w1_list)):
            w1_[i] = ord(w1_list[i])-ord('a')
            w2_[i] = ord(w2_list[i])-ord('a')
        if sum(w1_) == sum(w2_):
            print(word1+' and '+word2+' are anagram')
        else:
            print(word1+' and '+word2+' are not anagram')
                    
                
a = 'typhon'
b = 'python'
anagram_solution('python','tophon')

2, 判断数组、列表、字符串是否为空(空数组,空列表,空字符串)

直接判断:

通过True和False判断,a是空的,那么就是false(a=0也是false),所以if a不会执行。那么not a就是True




所以一般在代码中会出现

if a: 
	xxx   #a非空
else:
	return None #a是空返回None或0

当然对应的反过来就是if not a, 这在编程中很常见,如:

第六部分:python提高运算速度的方法

参考笔者的另一博文

第七部分:数据结构与算法

1 栈stack

1.1 python实现栈的功能

栈有以下功能:

stack(): #创建空栈
push(item)   #将元素推入栈顶,不返回信息
pop() #从栈顶移除一个元素并返回此元素
peek() #返回栈顶元素但不移除
is_empty() #测试是否是空栈,返回布尔值
size() #返回栈的大小

我们可以使用list来实现这个功能,可以选择list的任意一端作为栈的顶部,我们不妨选择list的末尾作为顶部,则


class Stack:
    
    def __init__(self):
        self.items = []
    
    def push(self,item):
        self.items.append(item)
        
    def pop(self):
        self.items.pop()
        
    def peek(self):
        return self.items[-1]
    
    def is_empty(self):
        return self.items == []
    
    def size(self):
        return len(self.items)
        
s = Stack()
print(s.is_empty())    
s.push('Dylan')
s.push(22)
print(s.peek())
print(s.size())

2 队列queue

2.1 python实现队列的功能

class Queue:
    def __init__(self):
        self.items = []
        
    def enqueue(self,item):
        return self.items.insert(0,item)
    
    def dequeue(self):
        return self.items.pop()
    
    def is_empty(self):
        return self.items == []
    
    def size(self):
        return len(self.items)

3 递归

3.1 递归给序列求和

def list_sum_rec(numlist):
    if len(numlist) == 1:
        return numlist[0]
    else:
        return numlist[0] + list_sum_rec(numlist[1:])
print(list_sum_rec([1,2]))

3.2 递归实现进制转换

def tostr(n,base):
    convertString = '0123456789ABCDEF'
    if n < base:
        return convertString[n]
    else:
        return tostr(n//base,base) + convertString[n%base]

print(tostr(4,2))

3.3 递归调用turtle内置包画图

import turtle

t = turtle.Turtle()
def draw_spiral(t,line_len):
    if line_len > 0:
        t.forward(line_len)
        t.right(90)
        draw_spiral(t,line_len-5)
draw_spiral(t,100)
turtle.done()

3.4 递归实现阶乘

def factorial(n):
    if n < 2:
        return 1
    else:
        return n*factorial(n-1)
    
print(factorial(3))

3.5 递归常见错误RecursionError

递归跟栈是紧密相连的,当调用函数时(递归实际上就是调用自身函数),系统会把调用时的现场数据(函数名,参数,返回值等)压入到系统调用栈中。所以如果栈被填满了,调用栈溢出,就会报错

def tell_story():
    print("从前有座山,山上有座庙,庙里有个老和尚,他在讲故事,他讲的故事是:")
    tell_story()

print("我给你讲个故事吧:")
tell_story()



另一方面,如果栈帧数太少,递归层数太少,基本结束条件演进太慢,也会出错。如上面的求和,如果调用栈帧只有1000个,那么1002个数的求和就会报错。
可以通过下面的方法查看和设定递归栈的层数:

import sys
sys.getrecursionlimit() 获取栈层数
>>> 3000     python默认3000
sys.setrecursionlimit(1000) 设置层数为1000
sys.getrecursionlimit()
>>> 1000 
import numpy as np

def list_sum_rec(numlist):
    if len(numlist) == 1:
        return numlist[0]
    else:
        return numlist[0] + list_sum_rec(numlist[1:])
print(list_sum_rec(list(np.arange(1002))))

由于我设置层数为1000,此时已经超过了层数(1002个数相加)

第八部分: 常用模块

1 numpy

1.1 numpy random生成随机数

参考笔者另一博文numpy生成随机数

1.2 numpy多维数组取反(反向排序,逆序)

一维数组

a = a[::-1] #直接取反

二维数组

三维数组
对第一个维度取反只需要展示第一个维度的反即可,或者也可以把后续维度用[:]也展示出来,

a[::-1],或者a[::-1,:,:] 


对第二个维度取反


如果所有维度都要取反,那么直接使用a[::-1,::-1,::-1]

2 sympy

2.1 符号计算

参考网站:网站1,网站2,网站3
计算积分: ∫ 1 2 d x ∫ 1 x x y d y \int_1^2dx\int_1^x xydy 12dx1xxydy
注意这个积分里面 y y y x x x的函数, y y y的积分限跟 x x x有关,因此要先积分掉 y y y再积分 x x x

当然也可以是用符号表示的定积分:


c = 1 ; d = 2 c=1; d=2 c=1;d=2带入就可以得到9/8.
也可以计算不定积分

以及无穷积分(两个o表示无穷大)

也可以是Integral给出表达式,然后doit()函数给出结果。

第九部分:数学基础

1 数据平滑处理

比如我有一个 sin ⁡ ( x ) \sin(x) sin(x)函数,对它做了一些处理(更改第三个数为-10),使得结果如下

使用scipy来平滑,

from scipy import signal
win = signal.hann(5)    #建立一个hann window
y1 = signal.convolve(y,win,mode='same')/sum(win)  #开始平滑,使得输出数据和原本数据的长度一样(same)
plt.plot(x,y1)
plt.show()  


上面使用了不同的hann window值来平滑。

2 数据拟合fit(多项式拟合)

参考笔者另一博文

第十部分:机器学习

10.1 tensorflow

安装教程很简单,见官网:tensorflow install(我的系统是linux)
上面红色表示要安装的tensorflow环境名,我这里为了方便记忆,写了的是enviroment_tf.

进入tensorflow环境
在安装完之后退出环境,以后想进入这个环境,需要重新source, 即进入enviroment_tf文件夹,然后用红色方框的命令进行source,之后就可以看到命令行前出现了(enviroment_tf)的字样,表示已经进入了TensorFlow环境中。

更多推荐

python教程笔记(详细)