Python基础

字符串
python的字符串可以用‘’或者“”括起来。

赋值符与浅拷贝
python中的赋值符只是指针赋值,而不是深拷贝。a=1, b=a, a=2, print(b)的结果是1。

除法
python中/除法的计算结果是浮点数,不论除数与被除数是什么类型。//除法为地板除,结果向下取整。
10/3=3.33333(float)
10//3=3(int)
10//3.0=3.0(float)

取整
a = numpy.array([…])
numpy.round(a) # 四舍五入
numpy.floor(a) # 向下取整
numpy.ceil(a) # 向上取整

循环
for v in (list/tuple)
for i in range(100) //for i = 0; i <100; ++i
while i <= 100
breakcontinue与C相同。

range函数
range(5) //0, 1, 2, 3, 4, 5
range(10, 15) //10, 11, 12, 13, 14
range(20, 10, -2) //20, 18, 16, 14, 12

类型检查
判断一个对象是否为特定类型:isinstance(object, type),返回布尔变量
isinstance(1, int) -> True
isinstance(2.34, list) -> False

main函数与全局变量
Python中写在if name == ‘main’里面的都是全局变量!
在自定义函数中使用全局变量需要用global声明!

Python中注释符号也需要缩进

Python基础IO

转义字符串
转义字符同C,可以用r’xxxxx’表示内部的字符串默认不转义
image.png
连续print多行内容
换行符\n的可读性差,可通过’’’xxx’’’表示多行内容
image.png
print的格式化输出
与C类似,包括%d、%f、%s、%x等(%05d、%-5d)
image.png
format()方法
用传入的参数依次替换字符串内的占位符{0}、{1}……
image.png
print的颜色

  1. print("\033[1;30m 字体颜色:白色\033[0m")
  2. print("\033[1;31m 字体颜色:红色\033[0m")
  3. print("\033[1;32m 字体颜色:深黄色\033[0m")
  4. print("\033[1;33m 字体颜色:浅黄色\033[0m")
  5. print("\033[1;34m 字体颜色:蓝色\033[0m")
  6. print("\033[1;35m 字体颜色:淡紫色\033[0m")
  7. print("\033[1;36m 字体颜色:青色\033[0m")
  8. print("\033[1;37m 字体颜色:灰色\033[0m")
  9. print("\033[1;38m 字体颜色:浅灰色\033[0m")
  10. print("背景颜色:白色 \033[1;40m \033[0m")
  11. print("背景颜色:红色 \033[1;41m \033[0m")
  12. print("背景颜色:深黄色 \033[1;42m \033[0m")
  13. print("背景颜色:浅黄色 \033[1;43m \033[0m")
  14. print("背景颜色:蓝色 \033[1;44m \033[0m")
  15. print("背景颜色:淡紫色 \033[1;45m \033[0m")
  16. print("背景颜色:青色 \033[1;46m \033[0m")
  17. print("背景颜色:灰色 \033[1;47m \033[0m")
  1. ![](https://cdn.nlark.com/yuque/0/2020/png/1333618/1596800198136-098a0e87-df23-40ad-8928-1faeb151c9b2.png#crop=0&crop=0&crop=1&crop=1&height=428&id=NJZct&originHeight=483&originWidth=254&originalType=binary&ratio=1&rotation=0&showTitle=false&size=0&status=done&style=none&title=&width=225)

读取键盘输入
通过input()函数读取用户输入,但是默认读取字符串类型

Python基础数据结构

list

  1. c = ['abc', 123, 4.567] #用方括号!可以放不同元素,是异质的
  2. len(c) #返回list中元素的个数
  3. c[-1] = 'jkl' #-1代表倒数第一个元素
  4. c.append('xyz') #在末尾添加元素
  5. c.insert(1, 'xyz') #在指定位置插入元素,其余元素后移
  6. c.pop() #删除末尾元素
  7. c.pop(1) #删除指定位置元素,其余元素前移

tuple

  1. t = ('abc', 123, 4.567) #用圆括号!和list一样可以放不同元素,是异质的
  2. t1 = (1), t2 = (1,) #t1是整数,构造单元素的tuple t2时,增加逗号消除歧义
  3. t[1] = 0 错误!!! #tuple与list的不同在于tuple是const的,不可变

tuple中存复杂元素时,存储的是目标的指针,该指针不可变但可以通过该指针改变复杂元素
image.png

dict
python中的dict类似于C++中的map,但是是异质的。

  1. d = { 'Ming': 95, 'Gang': 75, 'Hong': 85 } #用花括号
  2. d['Hua'] = 67 #增加元素或者更改元素
  3. d[1] = 'ABC' #可以使用各种类型作为key或者value
  4. if 'Hahaha' in d: #检测元素是否在dict中
  5. v = d.get('Hahaha') #返回特定键的值,不存在时返回None
  6. v = d.pop('Gang') #删除某个key

set
python中的set类似于C++中的set,但是是异质的。

  1. s = set([1, 2, 3, 4]) #可用list、tuple进行构造
  2. s.add(5),s.remove(1) #增添、删除元素
  3. s = s1 & s2 #取交集
  4. s = s1 | s2 #取并集


tuple(1,2,3)可作为dict与set的key,但tuple(1,[2,3])不可,因为存在可以改变的元素(即不可哈希项)

Python中的赋值

讨论如下代码块,在第二行中插入不同的赋值函数:

  1. import copy
  2. old = [1, [1, 2, 3], 3]
  3. (---assign function---)
  4. old[0] = 3
  5. old[1][0] = 3
  6. print(new)

非拷贝方法(直接赋值)

  1. new = old

输出结果:[3, [3, 2, 3], 3]

浅拷贝方法

  1. copy()函数

    1. new = copy.copy(old)
  2. 列表生成式

    1. new = [i for i in old]
  3. for循环

    1. new = []
    2. for i in range(len(old)):
    3. new.append(old[i])
  4. 切片

    1. new = old[:]

    输出结果:[1, [3, 2, 3], 3]

深拷贝方法

  1. new = copy.deepcopy(old)

输出结果:[1, [1, 2, 3], 3]

Python的高级特性

切片

  1. [begin=0 : end=len : stride=1] #======等同于C语言中的:
  2. for(int i=begin; i<len; i+=stride)
  3. L = [i for i in range(100)]
  4. L[:5] #0, 1, 2, 3, 4
  5. L[-3:] #97, 98, 99
  6. L[3:5] #3, 4
  7. L[:10:2] #0, 2, 4, 6, 8
  8. L[::20] #0, 20, 40, 60, 80

迭代

  1. L = [1,2,3]
  2. for v in L:
  3. do something
  4. T = (1,2,3)
  5. for v in T:
  6. do something
  7. D = {'A':1, 'B':2, 'C':3}
  8. for key in d:
  9. do something
  10. for val in d.values():
  11. do something
  12. for k, v in d.items():
  13. do something
  14. for i, val in enumerate(L): #使得循环过程中增加一个index
  15. print(i, val)
  16. >>>0 A
  17. >>>1 B
  18. >>>2 C
  19. #判断对象是否可迭代
  20. isinstance('abc', Iterable)
  21. >>>True
  22. isinstance([1,2,3], Iterable)
  23. >>>True
  24. isinstance(123, Iterable)
  25. >>>False

列表生成式

  1. L = [x*x for x in range(10,20) if x%2 == 0] #100,144,256,324
  2. D = {'x':A, 'y':B, 'z':C}
  3. LL = [k + '=' + v for k,v in d.items()] #['x=A','y=B','z=C']

生成器

与列表生成式类似,但是会实时计算被访问的元素而不会事先完成所有计算,可用于节省内存和开销。生成器中的元素必须被顺序访问,不支持下标访问。

  1. G = (x * x for x in range(10, 20) if x % 2 == 0) #使用小括号!
  2. print(G[2]) 错误!!! #Traceback!
  3. print(next(G)) #访问下一个G中的元素
  4. for v in G #通过迭代器遍历G

可以通过函数构造复杂的迭代器,在函数中使用关键词yield表示这不是一个普通函数而是一个用户构造迭代器的函数,函数执行到yield处进行一次元素返回。
以斐波那契数列迭代器为例:

  1. def fib(max):
  2. n, a, b = 0, 0, 1
  3. while n < max:
  4. yield b
  5. a, b = b, a+b
  6. n = n+1
  7. return 'done'
  8. #fib(6)可用于构造一个包含6个元素的生成器

迭代器

生成器是迭代器,而list、dict等虽然是Iterable的,却不是Iterator。Iterator不支持下标访问,只可用for迭代访问或通过next()访问下一个元素直到没有数据时抛出StopIteration错误。迭代器对象表示的是一个数据流,不可调用len函数获取长度,Iterator是惰性的,只有需要返回下一个数据时才会计算。
可以用iter()函数将Iterable的对象转换Iterator。

高阶函数

map()

map用于完成映射,接受两个参数,分别为映射函数和数据序列, 将映射函数作用于数据序列中的每一个元素。map返回的是一个Iterator!

  1. def f(x):
  2. return x*x
  3. r = map(f, [1,2,3,4,5]) #r = [1,4,9,16,25]
  4. rr = list(map(str, [1,2,3,4,5])) #rr = ['1','2','3','4','5']

reduce()

reduce用于将上一次的运算结果送入下一次迭代。reduce返回的是一个Iterator!

  1. # reduce(f, [x1,x2,x3,x4])=========f(f(f(x1,x2),x3),x4)
  2. def fn(x,y):
  3. return x*10 + y
  4. res = reduce(fn, [1,3,5,7,9]) #res = 13579

filter()

filter用于从序列中过滤出符合条件的元素。filter返回的是一个Iterator!

  1. def is_odd(n):
  2. return n % 2 == 1
  3. list(filter(is_odd), [1,2,3,4,5]) #[1,3,5]

装饰器

假设我们已经有一个函数Python笔记 - 图6,但是我们要增添Python笔记 - 图7的功能,又不想直接更改函数Python笔记 - 图8,譬如我们希望在每次卷积层前统计参数的分布等等。
下面是廖雪峰教程中的例子,在一个打印时间的函数执行前print一行当前调用的函数。

  1. def log(func):
  2. def wrapper(*args, **kw):
  3. print('call %s():' % func.__name__) # 先于now函数执行
  4. return func(*args, **kw) # now函数的执行
  5. return wrapper
  6. @log
  7. def now():
  8. print('2015-3-25')
  9. now() # 等效于now = log(now)
  10. >>> call now():
  11. >>> 2015-3-25

如果装饰器本身也有参数,则需要多一层嵌套装饰器:

  1. def log(text):
  2. def decorator(func):
  3. def wrapper(*args, **kw):
  4. print('%s %s():' % (text, func.__name__))
  5. return func(*args, **kw)
  6. return wrapper
  7. return decorator
  8. @log('execute')
  9. def now():
  10. print('2015-3-25')
  11. now() # 等效于now = log('execute')(now)
  12. >>> execute now():
  13. >>> 2015-3-25

面向对象编程

定义一个自定义类:
class Student(object)
括号中为继承的父类,若没有则使用object类

  1. 当没有构造函数__init__时,可以使用默认构造函数,通过Student()创建一个对象,否则必须根据构造函数要求的参数进行构造。构造函数不能重载只能有一个!<br /> 类的成员函数的第一个参数一定是self代表自己。

Python允许对实例动态绑定,同一个类的两个实例可能拥有完全不同的变量名:

  1. bart = Student('Bart Simpson', 59)
  2. lisa = Student('Lisa Simpson', 87)
  3. bart.age = 8
  4. print(bart.age)
  5. >>>8
  6. print(lisa.age)
  7. >>>Traceback (most recent call last):

类中的私有成员以__开头,外部无法访问

  1. def __init__(self, name, score):
  2. self.__name = name
  3. self.__score = score
  4. bart = Student('Bart Simpson', 59)
  5. print(bart.__name)
  6. >>>Traceback (most recent call last):
  7. bart.__name = 'New Name'
  8. print(bart.__name)
  9. >>>'New Name'
  10. #此时类内属性__name依然为'Bart Simpson'
  11. #实际上类内属性__name的真实名称为_Student__name
  12. #我们在外部相当于对bart这个实例动态绑定了一个新的变量__name
  1. class Student(object):
  2. name = 'Student'
  3. score = 0
  4. Student a, b
  5. a.age = 8
  6. #hasattr函数返回布尔类型,检测一个对象是否拥有特定属性
  7. hasattr(a, 'age')
  8. >>>True
  9. hasattr(b, 'age')
  10. >>>False
  11. #setattr函数能为对象添加一个特定属性并赋值,若对象已存在该属性则直接赋值
  12. setattr(b, 'age', 12)
  13. #getattr函数用于返回对象中特定属性的值,可以指定属性不存在时的默认返回参数
  14. getattr(a, 'salery')
  15. >>>Traceback (most recent call last):
  16. setattr(a, 'salery', 5000)
  17. getattr(a, 'salery')
  18. >>>>5000
  19. getattr(b, 'salery', 404)
  20. >>>404
  21. #以上的属性不局限于成员变量,同样可以是成员函数

image.png
从上面的例子可以看出,在编写程序的时候,千万不要对实例属性和类属性使用相同的名字,因为相同名称的实例属性将屏蔽掉类属性,但是当你删除实例属性后,再使用相同的名称,访问到的将是类属性。

面向对象高级编程

动态绑定添加方法

  1. from types import MethodType
  2. class Student():
  3. def __init__(self, name):
  4. self.name = name
  5. def set_age(self, age)
  6. self.age = age
  7. s = Student('Asuka')
  8. s.set_age = MethodType(set_age) # 为s这个实例添加set_age方法
  9. Student.set_age = set_age # 为Student类添加set_age方法,所有实例都可使用

slots

slots用于限制实例可在外部添加的属性:

  1. class Student():
  2. __slots__ = ('age', 'sex') # 作为一个tuple,限制在外部只能添加'age'和'sex'属性
  3. s = Student()
  4. s.age = 18 # is OK
  5. s.sex = 'Female' # is OK
  6. s.score = 98 # traceback, 'Student' object has no attribute 'score'

@property

@property可以将类的一个方法转化为属性进行访问。
廖雪峰教程中举得例子是,Student的score属性必须限定在0~100分中,如果直接将score属性暴露给外部则可能无法满足约束条件,除非增加get_score和set_score两个方法:

  1. class Student(object):
  2. def get_score(self):
  3. return self._score
  4. def set_score(self, value):
  5. if not isinstance(value, int):
  6. raise ValueError('score must be an integer!')
  7. if value < 0 or value > 100:
  8. raise ValueError('score must between 0 ~ 100!')
  9. self._score = value

这样显得非常麻烦,利用@property:

  1. class Student(object):
  2. @property
  3. def score(self):
  4. return self._score
  5. @score.setter
  6. def score(self, value):
  7. if not isinstance(value, int):
  8. raise ValueError('score must be an integer!')
  9. if value < 0 or value > 100:
  10. raise ValueError('score must between 0 ~ 100!')
  11. self._score = value

为类添加只读属性:

  1. class Screen(object):
  2. def __init__(self):
  3. self.width = 0
  4. self.height = 0
  5. @property
  6. def resolution(self):
  7. return self.width * self.height

定制类

  1. class Fibnacci(object):
  2. def __init__(self, count=10):
  3. self.a = 0
  4. self.b = 1
  5. self.count = 10
  6. self.cur = 0
  7. def __len__(self):
  8. return self.count
  9. def __str__(self):
  10. return 'Fibnacci object, {}/{} visited'.format(self.cur, self.count)
  11. __repr__ = __str__
  12. def __iter__(self): # 用于for ... in Fibnacci的迭代
  13. return self
  14. def __next__(self):
  15. if self.cur >= self.count:
  16. raise StopIteration()
  17. self.a, self.b = self.b, self.a + self.b
  18. self.cur += 1
  19. return self.a # 返回下一个值
  20. def __getitem__(self, n): # 用于构建list,下标索引
  21. a, b = 1, 1
  22. for x in range(n):
  23. a, b = b, a+b
  24. return a
  25. def __getattr__(self, attr): # 当实例中找不到外部访问的属性时,python会试图调用getattr获取
  26. if attr=='father': # 如果读不到实例中有father属性,则返回'lzy'
  27. return 'lzy'
  28. elif attr=='son': # 如果读不到实例中有son属性,则返回'lhm'
  29. return 'lhm'
  30. raise AttributeError('\'Fibnacci\' object has no attribute \'%s\' % attr)
  31. # __getattr__默认返回None,我们需要补上异常
  32. def __call__(self, **kwargs): # f=Fibnacci, f(...)的调用
  33. pass

静态方法和类方法

classmethod类似于C++中的静态成员函数,只能访问类属性而不能访问实例属性。staticmethod作为一个工具函数,不能访问类的属性也不能访问实例的属性。

  1. class cal():
  2. cal_name = '计算器'
  3. def __init__(self,x,y):
  4. self.x = x
  5. self.y = y
  6. @classmethod #@classmethon代表函数是类方法,只能访问类的数据属性,不能获取实例的数据属性
  7. def cal_info(cls): #python自动传入位置参数cls就是类本身
  8. print('这是一个%s'%cls.cal_name) #cls.cal_name调用类自己的数据属性
  9. @staticmethod #静态方法 类或实例均可调用
  10. def cal_test(a,b,c): #静态方法函数里不传入self或cls
  11. print(a,b,c)

绘制点图

  1. import matplotlib.pyplot as plt
  2. fig = plt.figure()
  3. ax = fig.add_subplot(1, 1, 1)
  4. ax.scatter(x, y) # x和y是list
  5. plt.show()

绘制分布图

  1. import seaborn as sns
  2. x = list(...)
  3. sns.distplot(x)

微信截图_20211115161108.png

init.py的使用

init.py的原始使命是声明一个模块,所以它可以是一个空文件。在init.py中声明的所有类型和变量,就是其代表的模块的类型和变量。我们在利用init.py时,应该遵循如下几个原则:
1、不要污染现有的命名空间。模块一个目的,是为了避免命名冲突,如果你在种用init.py时违背这个原则,是反其道而为之,就没有必要使用模块了。
2、利用init.py对外提供类型、变量和接口,对用户隐藏各个子模块的实现。一个模块的实现可能非常复杂,你需要用很多个文件,甚至很多子模块来实现,但用户可能只需要知道一个类型和接口。
假设我们的文件结构是这样的:

  1. └── main.py
  2. └── mypackage
  3. ├── test.py
  4. ├── sub1
  5. ├── test11.py
  6. └── test12.py
  7. ├── sub2
  8. ├── test21.py
  9. └── test22.py
  10. └── sub3
  11. ├── test31.py
  12. └── test32.py

test.py:

  1. from .sub1 import test11.py # boom, unresolved `sub1`

必须要给子文件夹加上init.py,python才会将子文件夹认为是一个包(package),才能import。文件夹中的init.py会在该包被import时调用(只会执行一次,不会因为多次被import就执行多次)。
加上init.py后的文件结构应该是这样的:

  1. └── main.py
  2. └── mypackage
  3. ├── test.py
  4. ├── __init__.py
  5. ├── sub1
  6. ├── __init__.py
  7. ├── test11.py
  8. └── test12.py
  9. ├── sub2
  10. ├── __init__.py
  11. ├── test21.py
  12. └── test22.py
  13. └── sub3
  14. ├── __init__.py
  15. ├── test31.py
  16. └── test32.py

mypackage.sub1.init.py:

  1. from . import test11
  2. from . import test12
  3. f1 = test11.f1
  4. f2 = test21.f2 # 这两行写了以后可以在main.py中mypackage.sub1.f1()这样
  5. # 不需要mypackage.sub1.test11.f1()
  6. # f1、f2成为了sub1这个package的module

mypackage.init.py:

  1. from . import sub1
  2. from . import sub2
  3. from . import sub3
  4. __all__ = ['sub1', 'sub2'] # 在main.py中from mypackage import * 时只有sub1、sub2会导入
  5. # 郑老师说__all__多写在功能py中不会写在__init__里