Python学习记录

整点数据处理。


#

本科的时候学的C#做GIS的二次开发,读研的时候发现数据处理才是最迫切的事,所以现在开始补一下 Python 3 上的内容。

# Python in VSCode

  1. 安装 Visual Studio Code
  2. VSCode 中安装 Python 插件
  3. 按照插件指引,安装 Python 最新版或者选择已有的 Python Interpreter
  4. 打开 cmd,输入 pip install yapf ,以安装 yapf
  5. 打开 VSCode 用户设置界面,搜索 formatting.provider 选择 yapf

快捷键:

 Shift + Alt + F 快速格式化代码。

 Ctrl + Enter 光标跳转到下一个新行。

# 算数运算

+  加   6+9=15
-  减   88-11=77
*  乘   2*3=6
/  除   6/3=2
=  赋值 a=2
// 取整 10//3=3
%  求余 10%3=1
** 幂    2**3=8
== 判断是否相等 1==2 return False
 * 还可以运用在字符串运算,”aaa”*10,就是输出10个aaa。 print(“a”*100) 

# 数据类型

Python 数据类型包括六个标准的数据类型——Number(数字)、String(字符串)、List(列表)、Tuple(元组)、Set(集合)、Dictionary(字典)

Number(数字):

Python 3 里,数字型变量包括——

  • int,整数数
  • float,浮点数
  • bool,布尔型
  • complex,复数——主要用于科学计算

C 语言相比,少了 double 类型,但在 Python 3 里用 float 就行了。

String(字符串):

字符串可以根据索引输出对应字符。

print (str)          # 输出字符串
print (str[:-1])    # 输出第一个到倒数第二个的所有字符
print (str[0])       # 输出字符串第一个字符
print (str[2:5])     # 输出从第三个开始到第五个的字符
print (str[2:])      # 输出从第三个开始的后的所有字符
print (str * 2)      # 输出字符串两次,也可以写成 print (2 * str)
print (str + "TEST") # 连接字符串

需要注意的是,索引和截取是不同的。要理解截取是按照一个区间来截取,而不是字符对应的第几位来截取。以菜鸟教程(Runoob)为例, str[1:2] 是下图[1,2]区间内包含的字符u。

字符串的一些常用方法:

# 获取字符串长度(字符串长度包括空格)
len(tiny_string)
# 统计某个字符串出现次数
tiny_string.count(string)
# 获取子字符串的位置,如果不存在该子字符串,程序会报错
tiny_string.index(substr)
# 判断字符串是否只包含空格(或空白字符\n\r\t等),若是则返回True
tiny_string.isspace()
# 判断字符串是否至少有一个字符且所有字符都是字母
tiny_string.isalpha()
# 判断数字(单纯的阿拉伯数字,以下三种方法都不能判断小数)
tiny_string.isdecimal()
# 判断数字(阿拉伯和Unicode的数字)
tiny_string.isdigit()
# 判断数字(阿拉伯、Unicode数字和汉字数字)
tiny_string.isnumeric()
# 判断字符串是否以指定的字符串开始或结束
tiny_string.startswith(substr)  tiny_string.endswith(substr)

字符串的查找和对齐:

# 从左向右查找指定字符串,和index方法很像,但在find方法中,若子字符串不存在,则返回-1。rfind方法就是从右向左开始查找
tiny_string.find(substr)
# 替换字符串,replace方法不会更改原字符串,会返回新的字符串,第三个参数是替换次数
tiny_string.replace(old_str, new_str, replace_times)
# 字符串向左、居中、向右对齐
poem_str.ljust(10, " ")    poem_str.center(10, " ")    poem_str.rjust(10, " ")
# 去除左边、右边或左右两边的空白字符
poem_str.lstrip()    poem_str.rstrip()    poem_str.strip()

字符串拆分、连接与切片:

# 拆分字符串,如果不加参数则默认以空格来分割字符串
str = "21fesf,fef1,ff,32f"
str_split = str.split(",")
# 合并字符串
str_bind = "".join(str_split)
# 字符串切片,步长的意思是,每执行一次移动多少,默认是1(向右移动1个),若设置为-1,就是向左移动1
str[开始索引:结束索引:步长]

注意:在字符串拼接时,有时需要用str()来强制转换为字符串才能进行拼接。

补充:DataFrame与多维数组的切片

该部分与 String 无关,只是讲到了切片顺便补充下。

# X为多维数组,df1为数据框
X = numpy.array([[1, 2, 3], [4, 5, 6]])
df1 = pandas.DataFrame(data=X, columns=['a', 'b', 'c'])
# 数据框需要使用loc[], iloc[]来切片
df2 = df1.loc[:, ['a', 'b']]    # .loc[]是根据column名来切
df3 = df1.iloc[:, [1, 2]]       # .iloc[]是根据索引来切,这是且第二列和第三列
# 多维数组没有loc[]和iloc[],直接开切,因为多维数组没有column名
arr1 = X[:, [1, 2]]    # 且第二列和第三列

List(列表):

列表在 Python 中使用得很频繁,可以实现大多数集合类的数据结构。列表元素类型可以不同,支持数字、字符串和列表(它本身)。列表写在 [ ] 之间,元素使用逗号隔开。列表也支持索引和截取,方法和字符串一样,这里不再赘述。

tiny_list = [25, "gis"]
list_demo = [69, "gisms", "m1", 123, tiny_list]
# 输出gisms和m1
print(list_demo[1:3])
# 输出123和tiny_list列表
print(list_demo[-2:])
# 第三个参数可表示截取的步长,如下是间隔一个取一个元素
list_demo[0:3:2]
# 增加元素,也可以往里增加列表
list_demo.append()
# 删除元素
list_demo.pop(index)    list_demo.remove(item)

条件逻辑

tiny_list = [5, 3, 53, 1, 2, 3, 5]
# new_list = [expression for member in iterable (if conditional)]
new_list = [i for i in tiny_list if i < 5 or i > 30]
new_list = [i for i in other_list if i in another_list]
# new_list = [expression (if conditional) for member in iterable]
new_list = [item if item < 5 else 0 for item in tiny_list]

Tuple(元组):

元组使用 ( ) 来定义,和列表不同的是,元组定义好后,元素不可更改。元组和列表可相互转换。

# 创建空元组
tiny_tuple = ()
# 定义只包含一个元素的元组
tiny_tuple = ("atuple",)
# 定义有多个元素的元组
tiny_tuple = ("a string", 123, True)
# 查看某元素在元组内出现几次
tiny_tuple.count(value")
# 将元组转为列表 或将列表转为元组
tuple_to_list = list(tiny_tuple)
list_to_tuple = tuple(tiny_list)

字符串、列表和元组都属于 sequence(序列)。

  • 与字符串一样,元组的元素不能修改
  • 元组也可以被索引和切片
  • 元组也可以使用+进行拼接
  • 注意构造包含 0 或 1 个元素的元组的特殊语法规则(以上4点来自菜鸟教程)

元组的应用场景:

  • 可以作为函数的参数返回值,一次接收任意多个参数或返回多个参数
  • 格式化字符串,如 str = “%s是一个由%d个元素组成的元组” % tiny_tuple

Dictionary(字典):

字典是无序的对象集合,字典中的元素通过键来存取。它是一种映射类型,用 { } 来标识,是一个无序的 键(key):值(value) 集合。

键必须使用不可变类型(Number、String、Tuple),在同一个字典中,键(key)必须是唯一的。

dict = {}
dict = {"name" : "GIS", "type" : "1", 2 : "two"}
# 获取name键的值
dict["name"]
# 获取字典的所有键
dict.keys()
# 获取字典的所有值
dict.values()

字典是可以进行修改的:

# 增加键值对,如果存在,则会修改
dict["site"] = "https://gis.ms"
# 删除指定key的键值对
dict.pop(2)
# 获取统计键值对数量
len(dict)
# 合并字典
dict.update(tiny_dict)
# 清空字典
dict.clear()
# 字典迭代遍历,变量item是每次循环中,获取到的键值对的key
for item in dict:
    print("%s - %s" % (item, dict[item]))

列表与字典

tiny_dict = {i: i / 2 for i in range(10)}
'''
{0: 0.0, 1: 0.5, 2: 1.0, 3: 1.5, 4: 2.0, 5: 2.5, 6: 3.0, 7: 3.5, 8: 4.0, 9: 4.5}
'''

公共运算符:

运算符Python表达式结果描述支持的数据类型
+[1,2]+[3,4][1,2,3,4]合并字符串、列表、元组
*[“H”] * 3[“H”, “H”, “H”]重复字符串、列表、元组
in1 in (1, 2, 3)True元素是否存在字符串、列表、元组、字典
not in1 not in (1,2,3)False元素是否不存在字符串、列表、元组、字典
> >= <= == <1 < 2True元素比较字符串、列表、元组

# 变量的格式化输出

在讲输出之前再简单说一下输入吧,用 input() 函数来输入。

salary = float(input("工资是:"))

注意, input() 函数输入的是字符串,需要转换成所需要的类型。

name = "W"
age = 12
print("My name is %s, and I'm %d yrs old." % (name, age))
%s  字符串
%d  整数。如果输入%06d,则是控制长度,不够的以0补全,如果超过6位,则该是多少就是多少。
%f  浮点数。使用%.2f来控制小数点后位数。
%%  输出%   # print("The scale is %.2f%%" % (scale*100))
%x  输出十六进制

a = 1.2123123
print('%.1f' % (a, ))

或者用 format() 函数进行格式化。

还有另一种方法可以快速输出不同格式变量:

a = 'aa'
b = 1
print(f'a is {a}, b is {b}')
print('a is %s, b is %d' % (a, b))
# 使用f''也可以快速指定小数位位数。下述代码为指定小数点位数后一位
print(f'a is {1.234:.1f}')

\t(转义字符):横向制表符,可以用来垂直方向对齐

row = 1
col = 1
while row <= 9:
    col = 1
    while col <= 9:
        print(row * col, end="\t")
        col += 1
        if col == 10:
            row += 1
            print("\n")

\n(转义字符):换行

\”(转义字符):输出”

\‘(转义字符):输出’

\\(转义字符):输出\

\r(转义字符):回车

使用反斜杠 \ 转义特殊字符,如果你不想让反斜杠发生转义,可以在字符串前面添加一个 r 表示原始字符串, print(r”原始\n字符串”) ,输出“原始\n字符串”

# 判断语句与逻辑运算

Python 判断 和之前学过的语言都不太一样,故记录一下。

if语句:

if 条件1:
    执行1
elif 条件2:
    # 在开发时不希望立刻编写分支里的代码,就可以写pass来保证代码结构正确
    pass
else:
    执行3

下面简单讲一下逻辑运算符:

# 与运算符 and
if 条件1 and 条件2:
    执行1
# 或运算符 or
if 条件1 or 条件2:
    执行1
# 非运算符 not
if not 条件1:
    执行1

# 导入模块

导入了模块之后就可以使用模块内的方法,有点像 R 中的 library() 

# 1石头 2剪刀 3布
# 写一个猜拳游戏
import random
human_player = int(input("请猜拳(1石头、2剪刀、3布):"))
bot_player = random.randint(1, 3)
if ((human_player == 1 and bot_player == 2)
        or (human_player == 2 and bot_player == 3)
        or (human_player ==3 and bot_player == 1)):
    print("电脑输入的是%d" % bot_player)
    print("玩家胜利")
elif human_player == bot_player:
    print("电脑输入的是%d" % bot_player)
    print("平局")
else:
    print("电脑输入的是%d" % bot_player)
    print("电脑胜利")

if 加一个大括号,就可以在 or 处按回车来进行换行。

# 循环语句

程序有三大流程——顺序、分支和循环。在上面讲了判断语句,这里简单讲讲循环语句。

while 循环中,也不像之前学习的编程语言那样,条件写在括号里,而是直接跟在后面。还有 Python 3 里没有 do{}while 的写法,若想实现该功能,需要使用其他方式。

while 条件:
    执行

Python 的循环中有 break continue 两个关键词。

  •  break 用在某一条件满足时,退出当前循环。
  •  continue 用在某一条件满足时,退出当次循环。

Python 3 中的 for 循环和 C 语言的 for 循环不同,前者用在非数字类型变量(列表、元组、字典、字符串)中从头到尾获取数据,属于迭代遍历。

for element in list:
    执行
# 完整的for循环
for 变量 in 集合:
    循环体代码
    if 条件:
        break
else:
    没有通过break退出循环,完整遍历完集合的每一个元素后才执行这里的代码

# 函数

使用 def 来定义函数。若想在另一个 Python 文件使用该函数,则需要导入函数所在的模块。

# 在func.py中输入函数
def multiplyab(a,b):
    return a*b
# 在main.py中使用func.py中的函数
import func
x = 1
y = 2
print(func.multiplyab(x, y))

值得注意的是,和 C 一样,在 Python 中也要先定义函数再调用函数。Python 甚至还规定了如何给函数加注释,我这乱写代码的有股下里巴人的味道了。

# 给函数加注释,应在函数下面一行使用""""""(三个引号)把对函数注释框起来。
# 如果不是对函数的描述,就用# 来加注释。
def func():
    """给函数加注释的格式"""
    # 返回
    return

缺省参数:

缺省参数是有默认值的参数,如果在调用函数时没有传入缺省参数则会使用参数默认值。指定缺省参数需要在定义时给参数赋默认值。缺省参数定义在函数参数列表的末尾,如果有多个缺省参数,则在调用时需要指定参数名。

def a_func(str1:str, str2 = "lalala"):
    执行

多值参数:

一个参数可以输入很多值。在函数处理的参数个数是不确定的时候就可以使用多值参数。在 Python 中有两种多值参数:

  • 参数名前加一个 * 可以接收元组
  • 参数名前加两个 * 可以接收字典

一般在给多值参数命名时用 *args 存放 元组 参数,用 **kwargs 存放 字典 参数。

def test_func(num, *args, **kwargs):
    for item in args:
        执行1
    执行2
# 调用,后两个参数为字典
test_func(1, "lala", 1, True, name="w", gender="m")

元组和字典拆包:

在调用带有多值参数的函数时,如果希望将一个元组变量直接传递给 args 或将一个字典变量直接传递给 kwargs ,就可以使用拆包,以简化参数的传递,拆包方式是:

  • 在元组变量前,增加一个 * 
  • 在字典变量前,增加两个 * 

如果不拆包的话,调用多值参数的函数时就得把元组的元素和字典的键值对一个一个输入。

gl_num = (1, 2, 3)
gl_info = {"name": "lala",
           "age": 18,
           "gender": "male"}
def test_func(*args, **kwargs):
    print(args)
    print(kwargs)
# 一个一个输入
test_func(1, 2, 3, name="lala", age= 18, gender="male")
# 使用拆包的方式输入
test_func(*gl_num, **gl_info)

函数的参数传递与返回值:

跟在形参后面的冒号,可以定义形参的数据类型。后面使用 -> 可以定义返回值的数据类型。

# 如果返回值包含多个值,则可以用元组的形式返回过去
def test_func(str:str, num:float) -> tuple:
    执行
    # 括号可以省略
    return (str, num)
result = test_func(tiny_string, tiny_num)
result[0] # 这样调用结果显然比较麻烦
# 可以使用多个变量一次接收函数的返回结果
gl_string, gl_num = test_func(tiny_str, tiny_num)
# 补充一个,交换a、b的值,等号右边是元组,当为元组时小括号可以省略
a, b = (b, a)

函数递归:

递归就是函数自己调用自己,如果要进行函数递归,则必须在函数里指明 出口,否则是死循环。

# 全局变量和局部变量

局部变量是在函数内部定义的变量,只能在函数内部使用。而全局变量在函数内外都能用。在函数内部使用赋值语句不能修改全局变量的值,如果使用 = 来赋值,则会在函数内部声明一个局部变量,并不会对全局变量造成影响。如果想修改全局变量,则需要使用 global 关键字在函数中定义一下全局变量再做修改。

gl_num = 1
def change_var():
    global gl_num
    gl_num = 2
change_var()
print(gl_num)

所有全局变量都应被定义在函数上方,这样函数才能访问。

需要注意的是,如果向函数传递的参数是 可变类型,在函数内部使用  Method() 修改数据内容是会影响到外部的数据的,但用等号赋值不会。

在函数中遇到 += 符号时需要注意,列表使用 += 的意思是调用 extend() ,调用了方法是会改变外部变量的。如果写成 tiny_list = tiny_list+ tiny_list,则不会影响外部变量,因为+=是调用的方法,而后者用了赋值符号。

代码结构:

  1. Shebang
  2. import模块
  3. 全局变量
  4. 函数定义
  5. 执行代码

# 面向对象

上面的变量、数据、函数都是对象,使用内置函数 dir() 传入标识符/数据,可以查看对象内的所有属性及方法。

之前遇到了一些以两个_开头并结尾的方法/属性,这是 Python 提供的 内置方法/属性

定义一个类并封装方法:

Python 中使用 class 来定义类,类中的方法第一个参数必须是 self。这个 self 是调用方法的对象的引用。

class 类名:
    def 方法1(self, 参数列表):
        pass
    def 方法2(self, 参数列表):
        pass

定义好类后,使用 对象变量 = 类名() 来创建对象。调用方法时不需要传递 self 参数,在方法内部可以通过 self 参数来访问对象的属性和其他的对象方法。

__init__(初始化)方法:

 __init__ 方法为 Python 内置的初始化方法,当对象创建时,会自动执行初始化方法。可以在 __init__ 方法内部定义属性,定义属性之后再使用类名创建的对象,就都有这个属性了。还可以才初始化时传值进去。

class test_demo:
    def __init__(self):
        self.属性名 = 属性的初始值
        self.name = "lalala"
        print("对象创建时自动执行的初始化代码")
示例:
class test_demo:
    def __init__(self, new_name):
        self.name = new_name
        print("对象创建时自动执行的初始化代码")
test = test_demo("this is the name")

__del__方法:

 __del__ 可以让对象被销毁前执行一些代码。

class test_demo:
    def __init__(self, new_name):
        self.name = new_name
        print("对象创建时自动执行的初始化代码")
    def __del__(self):
        print("我无了")

__str__方法:

如果想打印对象时打印自定义内容,就可以使用 __str__ 方法,该方法必须返回一个字符串。

class test_demo:
    def __init__(self, new_name):
        self.name = new_name
        print("对象创建时自动执行的初始化代码")
    def __str__(self):
        return "This is %s" % self.name
test = test_demo()
print(test)

封装:

  1. 封装是面向对象编程一大特点
  2. 面向对象编程的第一步——将 属性方法 封装到一个抽象的
  3. 外界用 创建 对象,让 对象 调用 方法
  4. 对象方法的细节都被 封装 到类的内部
class Person:
    def __init__(self, name, age, weight):
        self.name = name
        self.age = age
        self.weight = weight
    def __str__(self):
        return "My name is %s, and I'm %d yrs old. My weight is %d kg." % (self.name, self.age, self.weight)
    def eat(self):
        self.weight = self.weight + 1
    def lose_weight(self):
        self.weight = self.weight - 1
    def birthday(self):
        self.age = self.age + 1
cc = func.Person("cc", 10, 30)
cc.eat()
mm = func.Person("mm", 12, 30)
mm.lose_weight()
print(cc.weight)

身份运算符:

如果属性什么都没有,可以给属性设定一个 None 。在 Python 中针对 None 比较时,需要用到身份运算符,来比较两个对象的 内存地址 是否一致。

运算符描述实例
isis是判断两个标识符是不是引用同一个对象x is y,类似于id(x) == id(y)
is notis not是判断两个标识符是不是引用不同对象x is not y,类似于id(x) != id(y)

伪私有属性及方法:

如果想某些方法和属性只在对象内部使用而不想它们在外部被访问到,则需要定义私有属性、方法。定义方式是在在定义属性或方法时在属性名或方法前增加两个_。 __siyoushuxing 

定义好的私有属性和方法不能在外界直接访问,只能在对象内部使用。

但这也只是伪私有,在 Python 中不存在真正意义的私有,如果真想访问对象的私有属性和私有方法,还是可以访问得到的。

继承:

继承的语法 class 子类(父类名): 。子类继承父类,可以直接使用父类中已经封装好的方法,不需要再次开发。子类只需要再封装属于自己的属性和方法。

继承具有传递性,C类从B类继承,B类从A类继承,那么C不仅拥有B的属性和方法,还拥有A的属性和方法。但父类的私有属性和私有方法子类访问不了。

Override:

如果子类的方法与父类完全不同,就可以直接写一个同名方法就可以覆盖父类的方法。

class Animal:
    def bark(self):
        print("汪汪叫")
class Dog(Animal):
    def bark(self):
        print("lalala")

如果子类的方法仅仅对父类的这个方法进行扩展,则用 super(). 对子类的方法进行扩展。

class Animal:
    def bark(self):
        print("汪汪叫")
class Dog(Animal):
    def bark(self):
        super().bark()
        print("lalala")

多继承:

多继承就放在继承里讲吧。子类可以继承多个父类。 class A(B, C): ,定义时只需要在括号内把多个想要继承的类都加上。但如果多个父类存在相同的属性或方法,则尽量避免多继承。如果头铁用多继承,又想知道重名方法调用的是哪个父类的方法,则将 c.__mro__ print出来看看。

多态:

不同的子类对象调用相同的父类方法,产生不同的效果。是以继承和重写父类方法为前提的。

#

类是一个特殊的对象,也可以有属性。

类属性:

在类里定义属性,在整个类里都能够使用。以下代码就是使用类属性来输出整个类实例化了多少个对象。

class Animal:
    count = 0
    def __init__(self):
        Animal.count += 1
cat = Animal()
dog = Animal()
print(Animal.count)

类方法:

类方法是针对类对象定义的方法,在类方法内部可以直接访问类属性或调用其他类方法。类方法需要一个修饰器 @classmethod 来标识,来告诉解释器这是一个类方法。类方法的第一个参数是 cls 。由哪一个类调用的方法, cls 就是哪个类的引用,和实例方法的 self 是类似的。

通过 类名. 调用类方法时,不需要传递 cls 参数。在方法内部,可以通过 cls. 访问类的属性或调用其他的类方法。

class Animal():
    count = 0
    @classmethod
    def show_count(cls):
        print(cls.count)
    def __init__(self):
        Animal.count += 1

静态方法:

在开发时,如果需要在类中封装一个方法,而这个方法既不访问实例属性或调用实例方法,也不访问类属性或调用类方法,那么这个时候就可以给它封装成一个静态方法。使用时只需要使用 类名.静态方法() 

class AClass:
    @staticmethod
    def a_static_method():
        pass

如何确定使用哪种方法?

  • 实例方法 :方法内部需要访问实例属性,实例方法内部可以使用 类名. 访问类属性。
  • 类方法:方法内部只需要访问类属性。
  • 静态方法:方法内部不需要访问实例属性和类属性。

# 单例

设计模式:是前人工作的总结和提炼,被人们广泛流传的设计模式都是针对某一特定问题的成熟解决方案。

单例设计模式:让类创建对象,在系统中只有唯一一个实例,每一次执行 类名() 返回的对象内存地址是相同的。

__new__方法:

使用 类名() 创建对象时,Python 解释器首先会调用 __new__ 方法为对象分配空间。

 __new__ 是一个由 object 基类提供的内置静态方法,主要作用有两个:

  • 在内存中为对象分配空间
  • 返回对象的引用

Python 解释器获得对象的引用后,将引用作为第一个参数,传递给 __init__ 方法

重写 __new__ 方法以实现单例设计:

重写 __new__ 方法一定要return super().__new__(cls),否则解释器得不到分配空间了的对象引用,就不会调用对象的初始化方法。且 __new__ 是静态方法,在调用时需要主动传递 cls 参数。

class Bin:
    # 记录第一个被创建对象的引用
    instance = None
    def __new__(cls, *args, **kwargs):
        # 1.判断类属性是否为空对象
        if cls.instance is None:
            # 2.如果是空对象,则调用父类的方法,为第一个对象分配空间
            cls.instance = super().__new__(cls)
        # 3.返回类属性保存的对象引用
        return cls.instance

# 异常

抛出异常:程序停止执行并输出错误信息的动作。

try:
    执行1
except Exception as result:
    print("捕获到未知错误 %s" % Exception)
except 错误类型1:
    错误的处理
.
.
except (错误类型2, 错误类型3):
    错误的处理
else:
    在没有异常时执行
finally:
    无论有没有异常都执行

异常具有传递性,所以在主程序来捕获错误就可以了,注意力可以完全放在方法的开发上。

主动抛出异常:

Python 提供了一个 Exception 异常类,在开发时如果满足特定业务需求时,希望抛出异常可以先创建一个 Exception 对象,使用 raise 关键字抛出异常对象。

raise Exception("异常名称")

在循环中出现异常,报告该异常并进行补救,然后进行下一次循环:

for i in range(0, 20):
    df_fa_dict.update((key, []) for key in df_fa_dict)
    for idx, df in enumerate([df2000, df2001]):
        for key_str in ['pre_fa', 'tmx_fa', 'sif_fa']:
            df_fa_dict[key_str].append(df.iloc[i, :][key_str.split('_')[0]+str(idx+2000)+'fa'])
    data = pandas.DataFrame(df_fa_dict)
    try:
        pcorr_data = data.pcorr()
    except:
        # 如果出现错误,执行以下代码
        for key_str in ['sif_fa-pre_fa', 'sif_fa-tmx_fa']:
            row_dict[key_str].append(numpy.NaN)
        exception_list.append(i)
        # 此时不需要continue,出现错误捕获错误并执行相关代码,完成循环
    else:
        # 如果不出现异常,执行以下代码
        for key_str in ['sif_fa-pre_fa', 'sif_fa-tmx_fa']:
            row_dict[key_str].append(pcorr_data.at[key_str.split('-')[0], key_str.split('-')[1]])

# 模块

每引入一个模块需要占用一行代码,且可以对引入的模块定义别名。

import Module_2000_2000 as AModule
Amodule.method()

还可以从某一模块中导入一部分工具,使用 from…import 的方式实现。如果使用导入部分的方法导入工具,则直接使用导入的工具就好。如果从不同模块中使用局部导入的方法导入同名的函数,则会执行最新的。如果必须从多个模块调用同名函数,则需要起别名。

from Module_2000_2000 import 类
from Module_2000_2000 import 函数
instance = 类()
函数()
from Module_1 import func as module_1_func
from Module_2 import func as module_2_func

使用 from 模块 import * 的方式导入模块的所有工具,且使用时不用使用 模块. 的方式使用,但不推荐使用。

模块的搜素顺序:

  • 搜索 当前目录 指定名称的模块文件,如果有就直接导入
  • 如果没有,则搜索 系统目录
  • 所以在开发时,模块名不要和系统模块名重复

Python 中每一个模块都有一个 __file__ 内置属性,可以查看模块的完整路径。

模块开发原则:

模块被导入时,所有没有被缩进的代码都会被执行一边。所以如果不做处理,测试代码也会被执行一遍。解决这个问题只需要加一个判断。得益于 __name__ 属性,该属性若在模块内执行,则会返回 __main__,若作为导入的模块被执行,则会返回 模块名

# 导入模块
# 定义全局变量
# 定义类
# 定义函数
# 在代码最下方加测试代码
def main():
    pass
if __name__ == "__main__":
    main()

Package(包):

包是一个包含多个模块的特殊目录。包的结构是在一个小写字母加下划线为名称的目录中放一个 __init__.py ,再在包里放相关的模块,并在 __init__.py 里导入。导入方式如下:

# __init__.py
from . import module_name
# 导入包.py
import package_name
package_name.method()

使用包的话,可以把多个模块的功能杂糅在一块,并在 __init__.py 里指定暴露的函数,然后使用 包. 来调用函数。

使用pip安装第三方模块:

第三方模块是由知名第三方团队开发的被程序员广泛使用的包/模块。

# 安装
pip3 install pygame
# 卸载
pip3 uninstall pygame
# 使用http_proxy更新pip
python -m pip install --proxy http://127.0.0.1:2081 --upgrade pip

如果在 venv 中安装库,则需要进入Scripts文件夹使用 pip 安装。

# 文件操作

Python 有以下方法针对文件进行操作:

函数/方法说明
open打开文件并返回文件操作对象
read将文件内容读取到内存
write将指定内容写入文件
close关闭文件

一般来说操作文件有三个步骤:

# 1.打开文件,不加参数默认是r,只读方式打开
file = open("./test.txt")
# 2.读写文件
file.read()
# 3.关闭文件,一定要关闭文件,否则造成系统资源消耗
file.close()

需要注意的是Python存在一个文件指针,当使用了 read() 函数读取了文件后,文件指针会从开始移动到文件末尾。再次调用该函数后将读取不出任何内容。

open():

# 参数详见https://www.runoob.com/python3/python3-func-open.html
open(file, mode='r', buffering=-1, encoding=None, errors=None, newline=None, closefd=True, opener=None)

读取大文件使用readline():

# 1.打开文件
file = open("./test.txt")
# 2.读写文件
while True:
    # 3.读取一行内容
    text = file.readline()
    # 4.判断是否读取到内容
    if not text:
        break
    # 5.每读取一行输出一行。每读取一行自带\n
    print(text, end="")
# 3.关闭文件
file.close()

小文件复制:

# 1.以只读方式打开源文件
file_read = open("test.txt")
file_write = open("text_copy.txt", mode="w")
# 2.以读写方式打开目标文件
text = file_read.read()
file_write.write(text)
# 3.关闭
file_write.close()
file_read.close()

大文件复制:

# 1.以只读方式打开源文件
file_read = open("test.txt")
file_write = open("text_copy.txt", mode="w")
# 2.以行读写方式打开目标文件
while True:
    text_line = file_read.read()
    if not text_line:
        break
    # 每次写入文件指针都会被移动到末尾
    file_write = file_write.write(text_line)
# 3.关闭
file_write.close()
file_read.close()

将某个列表写入txt文件:

error_txt = open('error.txt', mode='w', encoding='utf-8')
for item in exception_list:
    error_txt.write(f'第{item+1}行出错,可能无法收敛\n')
zz_error_txt.close()

文件/目录的常用管理操作:

在 Python 中,实现创建、重命名、删除、改变路径、查看目录内容等操作。需要先导入 os 模块。

文件操作:

方法名说明示例
rename重命名文件os.rename(源文件名, 目标文件名)
remove删除文件os.remove(文件名)

目录操作:

方法名说明示例
listdir目录列表os.listdir(目录名)
mkdir创建目录os.mkdir(目录名)
rmdir删除目录os.rmdir(目录名)
getcwd获取当前目录os.getcwd()
chdir修改工作目录os.chdir(目标目录)
path.isdir判断是否为目录os.path.isdir(路径)

Python 2 中使用中文:

在第一行加一个单行注释就可以使用中文,但处理中文字符串还需要在冒号面前加一个小写的u

# *-* coding:utf-8 *-*
# 字符串中有中文
hello_world = u"你好世界"

# eval函数

 eval() 函数可以将 字符串当作有效的表达式 来运算并 返回计算结果

# 让用户输入一个算术式来计算并打印返回的结果
input_str = input("请输入算术式:")
print(eval(input_str))

在实际开发中慎用,因为这可以让用户执行任何终端命令。

# Lambda函数

该函数又称匿名函数,就是没有函数名的函数。该函数可以缩短代码,增加美观性。

# 这里dataframe.apply()里面填一个函数,如numpy.sum,numpy.sqrt。下面填的是lambda匿名函数。
data.loc[:, 'Price'] = data.loc[:, 'Price'].apply(lambda x: x / 10000)

def get_odd_even(x):
    if x % 2 == 0:
        return 'even'
    else:
        return 'odd'

get_odd_even = lambda x: 'even' if x % 2 == 0 else 'odd'
print(get_odd_even(5))

# 在Linux中安装最新版Python

1.更新及安装依赖

sudo apt update
sudo apt upgrade
# 安装额外的软件包以从源码安装Python
sudo apt install gcc build-essential gdb lcov pkg-config libbz2-dev libffi-dev libgdbm-dev libnss3-dev libgdbm-compat-dev liblzma-dev sqlite3 libncurses5-dev libreadline6-dev libsqlite3-dev wget libssl-dev lzma lzma-dev tk-dev uuid-dev zlib1g-dev

2.从官网下载Python的tar包并解压

# https://www.python.org/downloads/
tar -xvf python.tar.xz

3.进行配置与编译

# 配置
sudo ./configure --enable-optimizations --enable-shared
# 编译
sudo make

4.安装

一般软件使用 make install 进行安装,然而通过该方法安装最新版Python会覆盖旧版,破坏系统稳定。因此,在这里推荐使用 make altinstall ,这样可以使新旧版本并存。

sudo make altinstall

5.最后在终端输入pythonx.xx,将提示无法找到的.so文件软链接到/usr/lib/

之前开启动态链接库导致该文件无法找到,解决代码如下:

# 找到该.so文件的位置
whereis xxx.so
# 将该文件软链接到/usr/lib/
ln -s /xxx/xx.so /usr/lib/

6.更新Python、pip默认指向[OPTIONAL]

可以建立软链接并设置长命令的别名:

# 删除原来的软链接(若有的话)
rm /usr/bin/python
# 找到新版Python、pip可执行程序的位置
which pythonx.xx
which pip
# 为找到的位置建立软链接
ln -s /usr/local/bin/pythonx.xx /usr/bin/python
ln -s /usr/local/bin/pipx.xx /usr/bin/pip

或给新版Python、pip的较长命令取别名

# 打开~/.bashrc,在最后新增
alias python='/usr/local/bin/pythonx.xx'
alias pip='/usr/local/bin/pipx.xx'

接下来将记录一些实用的案例。

# 遍历列表时带上索引

for idx, item in enumerate(tiny_list):
    # 这里idx是从0开始的索引,可以表示遍历进度。

# 控制换行输出

flag_idx = 1
for idx, item in enumerate(a_list):
    if flag_idx % 10 == 0:
        end_var = '\n'
    else:
        end_var = ''
    flag_idx += 1
    print(f'{a_list[idx]}({b_list[idx]}), ', end=end_var)

# 获取文件夹内所有文件的文件名

需要使用自带的 os 库。

folder_path = 'D:/data/sd'
file_names = []
for file in os.listdir(folder_path):
    if not os.path.isdir(os.path.join(folder_path, file)):
        file_names.append(file)

# 转换列表内每个要素的数据类型

a = ['1', '2', '3']
a = [float(i) for i in a]

data_dict = {'alist': ['0.29', '0.12'], 'blist': ['12', '23']}
for key in data_dict:
    data_dict[key] = [float(i) for i in data_dict[key]]

# Return与Yield

 return 是返回数据的关键字,返回时结束整个函数的调用;而 yield 能够使函数在每一次循环中返回数据,而不中断整个函数的调用。

def data_iter(batch_size, features, labels):
    # 共有n个样本
    n = len(features)
    # 为这n个样本生成index
    indices = list(range(n))
    # 打乱index
    random.shuffle(indices)
    # 0为start,n为stop,batch_size为step
    for i in range(0, n, batch_size):
        # min()的意义在于,当i+batch_size超过样本量时,选择样本量
        batch_indices = torch.tensor(indices[i:min(i + batch_size, n)])
        yield features[batch_indices], labels[batch_indices]

# 可迭代对象与迭代器

为理解 PyTorch  DataLoader() ,补充这一章节。

首先,常用的 for loop 蕴藏两个核心概念,即可迭代对象与迭代器:

for <var> in <iterable>:
    <statement(s)>

其中可迭代对象 iterable 指的是可以一个一个返回它的元素的对象,如字典、元组、列表、字符串等。

而迭代器 iterator 是一个表示数据流的对象,可以使用 next() 不断从该对象中获取新数据。可迭代对象更像是数据的承载者,需要有能力产生迭代器;而迭代器不需要产生可迭代对象的能力。

实际上,在 for loop 循环的实现中,首先会从可迭代对象中拿出对应的迭代器,对应的是 the_iterator = iter(a_list) ,整个循环过程对应的是 next(the_iterator) 

a_list = [1, 2, 3]
# 实现_1
for i in a_list:
    print(i)
# 实现_2
the_iterator = iter(a_list)
print(next(the_iterator))
print(next(the_iterator))
print(next(the_iterator))
# 实现_3
the_iterator = iter(a_list)
for i in the_iterator:
    print(i)
Subscribe
Notify of
guest
0 Comments
Inline Feedbacks
View all comments