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
break与continue与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’表示内部的字符串默认不转义
连续print多行内容
换行符\n的可读性差,可通过’’’xxx’’’表示多行内容
print的格式化输出
与C类似,包括%d、%f、%s、%x等(%05d、%-5d)
format()方法
用传入的参数依次替换字符串内的占位符{0}、{1}……
print的颜色
print("\033[1;30m 字体颜色:白色\033[0m")
print("\033[1;31m 字体颜色:红色\033[0m")
print("\033[1;32m 字体颜色:深黄色\033[0m")
print("\033[1;33m 字体颜色:浅黄色\033[0m")
print("\033[1;34m 字体颜色:蓝色\033[0m")
print("\033[1;35m 字体颜色:淡紫色\033[0m")
print("\033[1;36m 字体颜色:青色\033[0m")
print("\033[1;37m 字体颜色:灰色\033[0m")
print("\033[1;38m 字体颜色:浅灰色\033[0m")
print("背景颜色:白色 \033[1;40m \033[0m")
print("背景颜色:红色 \033[1;41m \033[0m")
print("背景颜色:深黄色 \033[1;42m \033[0m")
print("背景颜色:浅黄色 \033[1;43m \033[0m")
print("背景颜色:蓝色 \033[1;44m \033[0m")
print("背景颜色:淡紫色 \033[1;45m \033[0m")
print("背景颜色:青色 \033[1;46m \033[0m")
print("背景颜色:灰色 \033[1;47m \033[0m")
![](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
c = ['abc', 123, 4.567] #用方括号!可以放不同元素,是异质的
len(c) #返回list中元素的个数
c[-1] = 'jkl' #-1代表倒数第一个元素
c.append('xyz') #在末尾添加元素
c.insert(1, 'xyz') #在指定位置插入元素,其余元素后移
c.pop() #删除末尾元素
c.pop(1) #删除指定位置元素,其余元素前移
tuple
t = ('abc', 123, 4.567) #用圆括号!和list一样可以放不同元素,是异质的
t1 = (1), t2 = (1,) #t1是整数,构造单元素的tuple t2时,增加逗号消除歧义
t[1] = 0 错误!!! #tuple与list的不同在于tuple是const的,不可变
tuple中存复杂元素时,存储的是目标的指针,该指针不可变但可以通过该指针改变复杂元素
dict
python中的dict类似于C++中的map,但是是异质的。
d = { 'Ming': 95, 'Gang': 75, 'Hong': 85 } #用花括号
d['Hua'] = 67 #增加元素或者更改元素
d[1] = 'ABC' #可以使用各种类型作为key或者value
if 'Hahaha' in d: #检测元素是否在dict中
v = d.get('Hahaha') #返回特定键的值,不存在时返回None
v = d.pop('Gang') #删除某个key
set
python中的set类似于C++中的set,但是是异质的。
s = set([1, 2, 3, 4]) #可用list、tuple进行构造
s.add(5),s.remove(1) #增添、删除元素
s = s1 & s2 #取交集
s = s1 | s2 #取并集
tuple(1,2,3)可作为dict与set的key,但tuple(1,[2,3])不可,因为存在可以改变的元素(即不可哈希项)
Python中的赋值
讨论如下代码块,在第二行中插入不同的赋值函数:
import copy
old = [1, [1, 2, 3], 3]
(---assign function---)
old[0] = 3
old[1][0] = 3
print(new)
非拷贝方法(直接赋值)
new = old
输出结果:[3, [3, 2, 3], 3]
浅拷贝方法
copy()函数
new = copy.copy(old)
列表生成式
new = [i for i in old]
for循环
new = []
for i in range(len(old)):
new.append(old[i])
切片
new = old[:]
输出结果:[1, [3, 2, 3], 3]
深拷贝方法
new = copy.deepcopy(old)
输出结果:[1, [1, 2, 3], 3]
Python的高级特性
切片
[begin=0 : end=len : stride=1] #======等同于C语言中的:
for(int i=begin; i<len; i+=stride)
L = [i for i in range(100)]
L[:5] #0, 1, 2, 3, 4
L[-3:] #97, 98, 99
L[3:5] #3, 4
L[:10:2] #0, 2, 4, 6, 8
L[::20] #0, 20, 40, 60, 80
迭代
L = [1,2,3]
for v in L:
do something
T = (1,2,3)
for v in T:
do something
D = {'A':1, 'B':2, 'C':3}
for key in d:
do something
for val in d.values():
do something
for k, v in d.items():
do something
for i, val in enumerate(L): #使得循环过程中增加一个index
print(i, val)
>>>0 A
>>>1 B
>>>2 C
#判断对象是否可迭代
isinstance('abc', Iterable)
>>>True
isinstance([1,2,3], Iterable)
>>>True
isinstance(123, Iterable)
>>>False
列表生成式
L = [x*x for x in range(10,20) if x%2 == 0] #100,144,256,324
D = {'x':A, 'y':B, 'z':C}
LL = [k + '=' + v for k,v in d.items()] #['x=A','y=B','z=C']
生成器
与列表生成式类似,但是会实时计算被访问的元素而不会事先完成所有计算,可用于节省内存和开销。生成器中的元素必须被顺序访问,不支持下标访问。
G = (x * x for x in range(10, 20) if x % 2 == 0) #使用小括号!
print(G[2]) 错误!!! #Traceback!
print(next(G)) #访问下一个G中的元素
for v in G #通过迭代器遍历G
可以通过函数构造复杂的迭代器,在函数中使用关键词yield表示这不是一个普通函数而是一个用户构造迭代器的函数,函数执行到yield处进行一次元素返回。
以斐波那契数列迭代器为例:
def fib(max):
n, a, b = 0, 0, 1
while n < max:
yield b
a, b = b, a+b
n = n+1
return 'done'
#fib(6)可用于构造一个包含6个元素的生成器
迭代器
生成器是迭代器,而list、dict等虽然是Iterable的,却不是Iterator。Iterator不支持下标访问,只可用for迭代访问或通过next()访问下一个元素直到没有数据时抛出StopIteration错误。迭代器对象表示的是一个数据流,不可调用len函数获取长度,Iterator是惰性的,只有需要返回下一个数据时才会计算。
可以用iter()函数将Iterable的对象转换Iterator。
高阶函数
map()
map用于完成映射,接受两个参数,分别为映射函数和数据序列, 将映射函数作用于数据序列中的每一个元素。map返回的是一个Iterator!
def f(x):
return x*x
r = map(f, [1,2,3,4,5]) #r = [1,4,9,16,25]
rr = list(map(str, [1,2,3,4,5])) #rr = ['1','2','3','4','5']
reduce()
reduce用于将上一次的运算结果送入下一次迭代。reduce返回的是一个Iterator!
# reduce(f, [x1,x2,x3,x4])=========f(f(f(x1,x2),x3),x4)
def fn(x,y):
return x*10 + y
res = reduce(fn, [1,3,5,7,9]) #res = 13579
filter()
filter用于从序列中过滤出符合条件的元素。filter返回的是一个Iterator!
def is_odd(n):
return n % 2 == 1
list(filter(is_odd), [1,2,3,4,5]) #[1,3,5]
装饰器
假设我们已经有一个函数,但是我们要增添的功能,又不想直接更改函数,譬如我们希望在每次卷积层前统计参数的分布等等。
下面是廖雪峰教程中的例子,在一个打印时间的函数执行前print一行当前调用的函数。
def log(func):
def wrapper(*args, **kw):
print('call %s():' % func.__name__) # 先于now函数执行
return func(*args, **kw) # now函数的执行
return wrapper
@log
def now():
print('2015-3-25')
now() # 等效于now = log(now)
>>> call now():
>>> 2015-3-25
如果装饰器本身也有参数,则需要多一层嵌套装饰器:
def log(text):
def decorator(func):
def wrapper(*args, **kw):
print('%s %s():' % (text, func.__name__))
return func(*args, **kw)
return wrapper
return decorator
@log('execute')
def now():
print('2015-3-25')
now() # 等效于now = log('execute')(now)
>>> execute now():
>>> 2015-3-25
面向对象编程
定义一个自定义类:
class Student(object)
括号中为继承的父类,若没有则使用object类
当没有构造函数__init__时,可以使用默认构造函数,通过Student()创建一个对象,否则必须根据构造函数要求的参数进行构造。构造函数不能重载只能有一个!<br /> 类的成员函数的第一个参数一定是self代表自己。
Python允许对实例动态绑定,同一个类的两个实例可能拥有完全不同的变量名:
bart = Student('Bart Simpson', 59)
lisa = Student('Lisa Simpson', 87)
bart.age = 8
print(bart.age)
>>>8
print(lisa.age)
>>>Traceback (most recent call last):
类中的私有成员以__开头,外部无法访问
def __init__(self, name, score):
self.__name = name
self.__score = score
bart = Student('Bart Simpson', 59)
print(bart.__name)
>>>Traceback (most recent call last):
bart.__name = 'New Name'
print(bart.__name)
>>>'New Name'
#此时类内属性__name依然为'Bart Simpson'
#实际上类内属性__name的真实名称为_Student__name
#我们在外部相当于对bart这个实例动态绑定了一个新的变量__name
class Student(object):
name = 'Student'
score = 0
Student a, b
a.age = 8
#hasattr函数返回布尔类型,检测一个对象是否拥有特定属性
hasattr(a, 'age')
>>>True
hasattr(b, 'age')
>>>False
#setattr函数能为对象添加一个特定属性并赋值,若对象已存在该属性则直接赋值
setattr(b, 'age', 12)
#getattr函数用于返回对象中特定属性的值,可以指定属性不存在时的默认返回参数
getattr(a, 'salery')
>>>Traceback (most recent call last):
setattr(a, 'salery', 5000)
getattr(a, 'salery')
>>>>5000
getattr(b, 'salery', 404)
>>>404
#以上的属性不局限于成员变量,同样可以是成员函数
从上面的例子可以看出,在编写程序的时候,千万不要对实例属性和类属性使用相同的名字,因为相同名称的实例属性将屏蔽掉类属性,但是当你删除实例属性后,再使用相同的名称,访问到的将是类属性。
面向对象高级编程
动态绑定添加方法
from types import MethodType
class Student():
def __init__(self, name):
self.name = name
def set_age(self, age)
self.age = age
s = Student('Asuka')
s.set_age = MethodType(set_age) # 为s这个实例添加set_age方法
Student.set_age = set_age # 为Student类添加set_age方法,所有实例都可使用
slots
slots用于限制实例可在外部添加的属性:
class Student():
__slots__ = ('age', 'sex') # 作为一个tuple,限制在外部只能添加'age'和'sex'属性
s = Student()
s.age = 18 # is OK
s.sex = 'Female' # is OK
s.score = 98 # traceback, 'Student' object has no attribute 'score'
@property
@property可以将类的一个方法转化为属性进行访问。
廖雪峰教程中举得例子是,Student的score属性必须限定在0~100分中,如果直接将score属性暴露给外部则可能无法满足约束条件,除非增加get_score和set_score两个方法:
class Student(object):
def get_score(self):
return self._score
def set_score(self, value):
if not isinstance(value, int):
raise ValueError('score must be an integer!')
if value < 0 or value > 100:
raise ValueError('score must between 0 ~ 100!')
self._score = value
这样显得非常麻烦,利用@property:
class Student(object):
@property
def score(self):
return self._score
@score.setter
def score(self, value):
if not isinstance(value, int):
raise ValueError('score must be an integer!')
if value < 0 or value > 100:
raise ValueError('score must between 0 ~ 100!')
self._score = value
为类添加只读属性:
class Screen(object):
def __init__(self):
self.width = 0
self.height = 0
@property
def resolution(self):
return self.width * self.height
定制类
class Fibnacci(object):
def __init__(self, count=10):
self.a = 0
self.b = 1
self.count = 10
self.cur = 0
def __len__(self):
return self.count
def __str__(self):
return 'Fibnacci object, {}/{} visited'.format(self.cur, self.count)
__repr__ = __str__
def __iter__(self): # 用于for ... in Fibnacci的迭代
return self
def __next__(self):
if self.cur >= self.count:
raise StopIteration()
self.a, self.b = self.b, self.a + self.b
self.cur += 1
return self.a # 返回下一个值
def __getitem__(self, n): # 用于构建list,下标索引
a, b = 1, 1
for x in range(n):
a, b = b, a+b
return a
def __getattr__(self, attr): # 当实例中找不到外部访问的属性时,python会试图调用getattr获取
if attr=='father': # 如果读不到实例中有father属性,则返回'lzy'
return 'lzy'
elif attr=='son': # 如果读不到实例中有son属性,则返回'lhm'
return 'lhm'
raise AttributeError('\'Fibnacci\' object has no attribute \'%s\' % attr)
# __getattr__默认返回None,我们需要补上异常
def __call__(self, **kwargs): # f=Fibnacci, f(...)的调用
pass
静态方法和类方法
classmethod类似于C++中的静态成员函数,只能访问类属性而不能访问实例属性。staticmethod作为一个工具函数,不能访问类的属性也不能访问实例的属性。
class cal():
cal_name = '计算器'
def __init__(self,x,y):
self.x = x
self.y = y
@classmethod #@classmethon代表函数是类方法,只能访问类的数据属性,不能获取实例的数据属性
def cal_info(cls): #python自动传入位置参数cls就是类本身
print('这是一个%s'%cls.cal_name) #cls.cal_name调用类自己的数据属性
@staticmethod #静态方法 类或实例均可调用
def cal_test(a,b,c): #静态方法函数里不传入self或cls
print(a,b,c)
绘制点图
import matplotlib.pyplot as plt
fig = plt.figure()
ax = fig.add_subplot(1, 1, 1)
ax.scatter(x, y) # x和y是list
plt.show()
绘制分布图
import seaborn as sns
x = list(...)
sns.distplot(x)
init.py的使用
init.py的原始使命是声明一个模块,所以它可以是一个空文件。在init.py中声明的所有类型和变量,就是其代表的模块的类型和变量。我们在利用init.py时,应该遵循如下几个原则:
1、不要污染现有的命名空间。模块一个目的,是为了避免命名冲突,如果你在种用init.py时违背这个原则,是反其道而为之,就没有必要使用模块了。
2、利用init.py对外提供类型、变量和接口,对用户隐藏各个子模块的实现。一个模块的实现可能非常复杂,你需要用很多个文件,甚至很多子模块来实现,但用户可能只需要知道一个类型和接口。
假设我们的文件结构是这样的:
└── main.py
└── mypackage
├── test.py
├── sub1
│ ├── test11.py
│ └── test12.py
├── sub2
│ ├── test21.py
│ └── test22.py
└── sub3
├── test31.py
└── test32.py
test.py:
from .sub1 import test11.py # boom, unresolved `sub1`
必须要给子文件夹加上init.py,python才会将子文件夹认为是一个包(package),才能import。文件夹中的init.py会在该包被import时调用(只会执行一次,不会因为多次被import就执行多次)。
加上init.py后的文件结构应该是这样的:
└── main.py
└── mypackage
├── test.py
├── __init__.py
├── sub1
│ ├── __init__.py
│ ├── test11.py
│ └── test12.py
├── sub2
│ ├── __init__.py
│ ├── test21.py
│ └── test22.py
└── sub3
├── __init__.py
├── test31.py
└── test32.py
mypackage.sub1.init.py:
from . import test11
from . import test12
f1 = test11.f1
f2 = test21.f2 # 这两行写了以后可以在main.py中mypackage.sub1.f1()这样
# 不需要mypackage.sub1.test11.f1()
# f1、f2成为了sub1这个package的module
mypackage.init.py:
from . import sub1
from . import sub2
from . import sub3
__all__ = ['sub1', 'sub2'] # 在main.py中from mypackage import * 时只有sub1、sub2会导入
# 郑老师说__all__多写在功能py中不会写在__init__里