最近自学一下python,
边看边做一下笔记,主要以代码为主。
理论和描述请移步:
https://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000
语法
List&tuple
"""
1、cmp(list1, list2):比较两个列表的元素
2、len(list):列表元素个数
3、max(list):返回列表元素最大值
4、min(list):返回列表元素最小值
5、list(seq):将元组转换为列表
列表操作包含以下方法:
1、list.append(obj):在列表末尾添加新的对象
2、list.count(obj):统计某个元素在列表中出现的次数
3、list.extend(seq):在列表末尾一次性追加另一个序列中的多个值(用新列表扩展原来的列表)
4、list.index(obj):从列表中找出某个值第一个匹配项的索引位置
5、list.insert(index, obj):将对象插入列表
6、list.pop(obj=list[-1]):移除列表中的一个元素(默认最后一个元素),并且返回该元素的值
7、list.remove(obj):移除列表中某个值的第一个匹配项
8、list.reverse():反向列表中元素
9、list.sort([func]):对原列表进行排序
"""
arr = ['a', 'b', 'c']
arr.append('d') # 添加元素
print('取最后一个元素:' + arr[-1])
print('删除并取出某元素:' + arr.pop(0))
print('删除末尾元素:' + arr.pop())
arr = ['a', 'b', 'c']
# 反向排序
arr.reverse()
# 反向排序
arr.sort(reverse=True)
for obj in arr:
print(obj)
#临时排序
arr2=sorted(arr)
#列表解析
arr = [val+1 for val in range(1,11)]
print(arr)
## 切片
arr = ['a', 'b', 'c']
print(arr[0:1])
print(arr[1:])
print(arr[:2])
# 字符串同样可以切片
str = 'abc'
print(str[0:1])
# 复制列表,如果arr=arr2,那么arr和arr2的id是相同的
arr = ['a', 'b', 'c']
arr2 = arr[:]
print(id(arr))
print(id(arr2))
另一种有序列表叫元组:tuple。tuple和list非常类似,但是tuple一旦初始化就不能修改,它也没有append(),insert()这样的方法。其他获取元素的方法和list是一样的,你可以正常地使用classmates[0]
,classmates[-1]
,但不能赋值成另外的元素。
不可变的tuple有什么意义?因为tuple不可变,所以代码更安全。如果可能,能用tuple代替list就尽量用tuple。
t =('a','b','a')
dist&set
dist = {}
dist['a'] = 1
dist['b'] = 2
# print(dist['c'])#不存在key会报错
print(dist.get('c')) # 不存在key返回None
print(dist.get('c', '3')) # 不存在key返回指定value
# 判断key是否存在
print('a' in dist)
dist.pop('a') # 删除
和java的map差不多,另请务必注意,dict内部存放的顺序和key放入的顺序是没有关系的。
和list比较,dict有以下几个特点:
- 查找和插入的速度极快,不会随着key的增加而变慢;
- 需要占用大量的内存,内存浪费多。
而list相反:
- 查找和插入的时间随着元素的增加而增加;
- 占用空间小,浪费内存很少。
所以,dict是用空间来换取时间的一种方法。
set
set和dict类似,也是一组key的集合,但不存储value。由于key不能重复,所以,在set中,没有重复的key。
s = {1, 2, 3, 3}
s.add(3) # 重复插入无效
s.remove(2)
print(s)
循环
#用得最多的两个循环,支持List dist set tuple
arr = ['a', 'b', 'c']
for a in arr:
print(a)
for i, a in enumerate(arr):
print(i,a)
#遍历dist,key val方式
dist = {}
dist['a'] = 1
dist['b'] = 2
for key,val in dist.items():
print(key,val)
定义函数
固定参数
def power(x):
return x * x
默认参数
#必选参数在前,默认参数在后,否则Python的解释器会报错
def power(a, b=1):
return a * b
print(power(2,2))
print(power(2))
另外值得注意的是,默认参数会有一个坑:
def add_end(L=[]):
L.append('END')
return L
# 结果正常:['END']
print(add_end())
# 结果异常:['END', 'END']
print(add_end())
很多初学者很疑惑,默认参数是[]
,但是函数似乎每次都“记住了”上次添加了END
后的list。
原因解释如下:
Python函数在定义的时候,默认参数L
的值就被计算出来了,即[]
,因为默认参数L
也是一个变量,它指向对象[]
,每次调用该函数,如果改变了L
的内容,则下次调用时,默认参数的内容就变了,不再是函数定义时的[]
了。
定义默认参数要牢记一点:默认参数必须指向不变对象!
不可变(immutable):int、字符串(string)、float、(数值型number)、元组(tuple) 、None也算
可变(mutable):字典型(dictionary)、列表型(list)
正确写法:
def add_end(L=None):
if L is None:
L = []
L.append('END')
return L
可变参数
"""
在函数内部,参数numbers接收到的是一个tuple,
因此,函数代码完全不变。但是,调用该函数时,可以传入任意个参数,包括0个参数:
"""
def test(*numbers):
for num in numbers:
print(num)
test(1, 2, 3)
#已有List或tuple调用方法
nums = [1, 2, 3]
test(*nums)
关键字参数
"""
可变参数允许你传入0个或任意个参数,这些可变参数在函数调用时自动组装为一个tuple。
而关键字参数允许你传入0个或任意个含参数名的参数,
这些关键字参数在函数内部自动组装为一个dict
"""
def person(name, age, **kw):
print('name:', name, 'age:', age, 'other:', kw)
person('小明', 1, city='广州')
"""
已有dist调用:
**extra表示把extra这个dict的所有key-value用关键字参数传入到函数的**kw参数,
kw将获得一个dict,注意kw获得的dict是extra的一份拷贝,对kw的改动不会影响到函数外的extra。
"""
extra = {'city': '广州', 'job': 'java'}
person('小明', 1, **extra)
命名关键字参数
"""
对于关键字参数,函数的调用者可以传入任意不受限制的关键字参数。至于到底传入了哪些,就需要在函数内部通过kw检查。
如果要限制关键字参数的名字,就可以用命名关键字参数,例如,只接收city和job作为关键字参数。这种方式定义的函数如下:
"""
# 和关键字参数**kw不同,命名关键字参数需要一个特殊分隔符*,*后面的参数被视为命名关键字参数。
def person(name, age, *, city, job):
print(name, age, city, job)
person('Jack', 24, city='Beijing', job='Engineer')
#报错
person('Jack', 24, city='Beijing', other='Engineer')
# 命名关键字参数 也支持默认参数
def person(name, age, *, city='广州', job):
print(name, age, city, job)
组合参数
在Python中定义函数,可以用必选参数、默认参数、可变参数、关键字参数和命名关键字参数,这5种参数都可以组合使用。
但是请注意,参数定义的顺序必须是:必选参数、默认参数、可变参数、命名关键字参数和关键字参数。
虽然可以组合多达5种参数,但不要同时使用太多的组合,否则函数接口的可理解性很差。
def f1(a, b, c=0, *args, **kw):
print('a =', a, 'b =', b, 'c =', c, 'args =', args, 'kw =', kw)
异常与测试
try catch
try:
print('try...')
r = 10 / int('2')
print('result:', r)
except ValueError as e:
print('ValueError:', e)
except ZeroDivisionError as e:
print('ZeroDivisionError:', e)
else:
print('no error!')
finally:
print('finally...')
print('END')
#PS:
1.Python的错误其实也是class,所有的错误类型都继承自BaseException
2.raise和java中的throw是一样的 raise ValueError('invalid value: %s' % s)
单元测试
import unittest
"""
特别注意:
1.文件名不能为 test.py
2.文件名带有test字样 IDE(pyCharm)才会识别到这是一个测试类,右击下面的测试方法运行才会单独运行该方法
"""
class TestDict(unittest.TestCase):
def setUp(self):
print('单元测试前执行')
def tearDown(self):
print('单元测试后执行')
# 以test开头的方法就是测试方法,不以test开头的方法不被认为是测试方法,测试的时候不会被执行。
def test1(self):
# IDE 右键运行,单元测试该方法
print('test1')
def test_2(self):
print('test2')
if __name__ == '__main__':
# 运行所有单元测试
unittest.main()
高级函数
map
map()函数接收两个参数,一个是函数,一个是Iterable,map将传入的函数依次作用到序列的每个元素,并把结果作为新的Iterator返回。
"""
利用map()函数,把用户输入的不规范的英文名字,
变为首字母大写,其他小写的规范名字。
输入:['adam', 'LISA', 'barT'],输出:['Adam', 'Lisa', 'Bart']:
"""
def normalize(name):
return name.lower().title()
L1 = ['adam', 'LISA', 'barT']
L2 = list(map(normalize, L1))
print(L2)
filter
Python内建的filter()
函数用于过滤序列。
和map()
类似,filter()
也接收一个函数和一个序列。和map()
不同的是,filter()
把传入的函数依次作用于每个元素,然后根据返回值是True
还是False
决定保留还是丢弃该元素。
例如,在一个list中,删掉偶数,只保留奇数,可以这么写:
def is_odd(n):
return n % 2 == 1
list(filter(is_odd, [1, 2, 4, 5, 6, 9, 10, 15]))
# 结果: [1, 5, 9, 15]
lambda
#将刚刚map的例子改成使用lambda
L1 = ['adam', 'LISA', 'barT']
L2 = list(map(lambda name: name.lower().title(), L1))
print(L2)
swith
python没有swith函数,但可以通过以下方式实现
swich = {
1: lambda x: x + 1,
2: lambda x: x + 2,
3: lambda x: x + 3,
}
print(swich[1](1))
print(swich[2](1))
print(swich[3](1))
#不传参可以这样写
swich = {
1: lambda: 'case1',
2: lambda: 'case2',
3: lambda: 'case3',
}
print(swich[1]())
print(swich[2]())
print(swich[3]())
sorted
import datetime
#简单的排序
sorted([36, 5, -12, 9, -21])
#反序
sorted([36, 5, -12, 9, -21],reverse=True)
#时间排序的例子
result_data = []
result_data.append({
'id': 2,
'create_time': '2012-10-8 11:09:22',
})
result_data.append({
'id': 3,
'create_time': '2011-10-10 11:09:22',
})
result_data.append({
'id': 1,
'create_time': '2013-10-1 11:09:22',
})
def date_sort(data_str):
a_datetime = datetime.datetime.strptime(data_str, '%Y-%m-%d %H:%M:%S')
return a_datetime.timestamp()
print(sorted(result_data, key=lambda item: date_sort(item['create_time'])))
decorator(修饰器)
和java的AOP有点类似吧
比如在不修改已有函数的情况下,给这个函数执行前后打日志:
import functools
def log(func):
@functools.wraps(func)
def wrapper(*args, **kw):
print('前置:', func.__name__)
result = func(*args, **kw)
print('后置')
return result
return wrapper
@log
def now():
print('--now函数 逻辑---')
return '2017-01-01'
print('现在时间:' + now())
# @functools.wraps(func)的作用:
# 因为返回的那个wrapper()函数名字就是'wrapper',
# 所以,需要把原始函数的__name__等属性复制到wrapper()函数中,否则,有些依赖函数签名的代码执行就会出错。
# 加了这句代码:print(now.__name__) 结果 now()
# 没加这句代码:print(now.__name__) 结果 wrapper()
把@log
放到now()
函数的定义处,相当于执行了语句:
now = log(now)
如果decorator本身需要传入参数,那就需要编写一个返回decorator的高阶函数,写出来会更复杂。比如,要自定义log的文本
def logger(text):
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kw):
print('%s %s():' % (text, func.__name__))
return func(*args, **kw)
return wrapper
return decorator
# 相当于执行了 now = logger('DEBUG')(today)
@logger('DEBUG')
def today():
print('2015-3-25')
我们来剖析上面的语句,首先执行log(execute)
,返回的是decorator
函数,再调用返回的函数,参数是now
函数,返回值最终是wrapper
函数。
面向对象
类的创建与继承
class Animal(object):
# 类属性,类的所有实例,包括子类都可以访问到
other = '类属性'
# 类似java的构造函数
def __init__(self, name, age):
# pulic 属相
self.name = name
# private 属相
self.__age = age
def run(self):
print('Animal is running...')
#继承
class Dog(Animal):
def call(self):
print('汪汪汪')
def run(self):
print('狗走路')
# test
dog = Dog('旺财', 1)
dog.run()
dog.call()
print(isinstance(dog, Animal)) # true
print(isinstance(dog, Dog)) # true
使用__slots__
如果我们像拓展一个类或实例的属性和方法,可以这么做:
from types import MethodType
class Animal(object):
pass
a = Animal()
# 已存在的实例绑定一个新属性
a.name = '狗'
# 已存在的实例绑定一个新方法
def run(self):
print('走路')
a.run = MethodType(run, a)
a.run()
# 给class绑定新方法,其所有实例都可以使用
Animal.run2 = run
a.run2()
但是,如果我们想要限制实例的属性怎么办?比如,只允许对实例Animal添加name、age
属性。
这时候我们定义类可以使用__slots__ 来达到目的
class Animal(object): #只允许拓展name、age属性,否则会报错 __slots__ = (name, age)
__slots__定义的属性仅对当前类实例起作用,对继承的子类是不起作用的
除非在子类中也定义__slots__
,这样,子类实例允许定义的属性就是自身的__slots__
加上父类的__slots__
使用property
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2018/3/19 16:52
# @Author : HeyS1
""" """
"""
@property广泛应用在类的定义中,可以让调用者写出简短的代码,
同时保证对参数进行必要的检查,这样,程序运行时就减少了出错的可能性。
"""
# 常规写法
class Student1(object):
def get_score(self):
return self._score
def set_score(self, value):
# 做一些参数验证....
self._score = value
s1 = Student1()
s1.set_score(100)
print(s1.get_score())
# 简洁写法
class Student2(object):
@property
def score(self):
return self._score
@score.setter
def score(self, value):
# 做一些参数验证....
self._score = value
@property # 只定义getter方法,不定义setter方法就是一个只读属性:
def age(self):
pass
s2 = Student2()
s2.score = 99 # 这里就直接赋值,实际转化为s.set_score(99)
print(s2.score)
枚举
定义常量时,使用枚举类型去定义比普通的声明变量更优
from enum import unique, Enum
@unique # 此装饰器可以帮助我们检查保证没有重复值
class Const(Enum):
A = 'a'
B = 'b'
# 调用:
print(Const.A)
print(Const.A.value)
print(Const('a'))