Python 实现对函参做类型检查
Python
自带的函数一般都会有对函数参数类型做检查,自定义的函数参数类型检查可以用函数 isinstance()
实现,例如:
def my_abs(x):
"""
自定义的绝对值函数
:param x: int or float
:return: positive number, int or float
"""
if not isinstance(x, (int, float)):
raise TypeError('bad operand type')
if x > 0:
return x
else:
return -x
添加了参数检查后,如果传入错误的参数类型,函数就可以抛出一个 TypeError
错误。
为什么说 Python 是动态语言
在 Python
中,等号 =
是赋值语句,可以把任意数据类型
赋值给变量,同样一个变量可以反复赋值,而且可以是不同类型的变量,例如:
a = 100 # a是int型变量
print(a)
a = 'ABC' # a 是str型变量
print(a)
Pyhon 这种变量本身类型不固定,可以反复赋值不同类型的变量称为动态语言,与之对应的是静态语言。静态语言在定义变量时必须指定变量类型,如果赋值的时候类型不匹配,就会报错,Java/C++ 都是静态语言(int a; a = 100
)
Python 装饰器理解
装饰器本质上是一个 Python 函数或类,它可以让其他函数或类在不需要做任何代码修改的前提下增加额外功能,装饰器的返回值也是一个函数/类对象。它经常用于有切面需求的场景,比如:插入日志、性能测试、事务处理、缓存、权限校验等场景,装饰器是解决这类问题的绝佳设计。有了装饰器,我们就可以抽离出大量与函数功能本身无关的雷同代码到装饰器中并继续重用。概括的讲,装饰器的作用就是为已经存在的对象添加额外的功能。
map 与 reduce 函数用法解释
1、map()
函数接收两个参数,一个是函数,一个是 Iterable,map 将传入的函数依次作用到序列的每个元素,并将结果作为新的 Iterator 返回,简单示例代码如下:
# 示例1
def square(x):
return x ** 2
r = map(square, [1, 2, 3, 4, 5, 6, 7])
squareed_list = list(r)
print(squareed_list) # [1, 4, 9, 16, 25, 36, 49]
# 使用lambda匿名函数简化为一行代码
list(map(lambda x: x * x, [1, 2, 3, 4, 5, 6, 7, 8, 9]))
# 示例2
list(map(str, [1, 2, 3, 4, 5, 6, 7, 8, 9])) # ['1', '2', '3', '4', '5', '6', '7', '8', '9']
注意map函数返回的是一个Iterator(惰性序列),要通过list函数转化为常用列表结构。map()作为高阶函数,事实上它是把运算规则抽象了。
2、reduce()
函数也接受两个参数,一个是函数(两个参数),一个是序列,与 map
不同的是reduce 把结果继续和序列的下一个元素做累积计算,效果如下:
reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)
示例代码如下:
from functools import reduce
CHAR_TO_INT = {
'0': 0,
'1': 1,
'2': 2,
'3': 3,
'4': 4,
'5': 5,
'6': 6,
'7': 7,
'8': 8,
'9': 9
}
def str2int(str):
ints = map(lambda x:CHAR_TO_INT[x], str) # str对象是Iterable对象
return reduce(lambda x,y:10*x + y, ints)
print(str2int('0'))
print(str2int('12300'))
print(str2int('0012345')) # 0012345
Python 深拷贝、浅拷贝区别
Python 中的大多数对象,比如列表 list
、字典 dict
、集合 set
、numpy
数组,和用户定义的类型(类),都是可变的。意味着这些对象或包含的值可以被修改。但也有些对象是不可变的,例如数值型 int
、字符串型 str
和元组 tuple
。
1、复制不可变数据类型:
复制不可变数据类型,不管 copy
还是 deepcopy
, 都是同一个地址。当浅复制的值是不可变对象(数值,字符串,元组)时和=“赋值”的情况一样,对象的 id
值与浅复制原来的值相同。
2、复制可变数据类型:
- 直接赋值:其实就是对象的引用(别名)。
- 浅拷贝(
copy
):拷贝父对象,不会拷贝对象内部的子对象(拷贝可以理解为创建内存)。产生浅拷贝的操作有以下几种:
- 使用切片
[:]
操作
- 使用工厂函数(如
list/dir/set
), 工厂函数看上去像函数,实质上是类,调用时实际上是生成了该类型的一个实例,就像工厂生产货物一样.
- 使用
copy
模块中的 copy()
函数,b = a.copy()
, a
和 b
是一个独立的对象,但他们的子对象还是指向统一对象(是引用)。
- 深拷贝(
deepcopy
): copy 模块的 deepcopy()
方法,完全拷贝了父对象及其子对象,两者是完全独立的。深拷贝,包含对象里面的子对象的拷贝,所以原始对象的改变不会造成深拷贝里任何子元素的改变。
注意:浅拷贝和深拷贝的不同仅仅是对组合对象来说,所谓的组合对象(容器)就是包含了其它对象的对象,如列表,类实例。而对于数字、字符串以及其它“原子”类型(没有子对象),没有拷贝一说,产生的都是原对象的引用。更清晰易懂的理解,可以参考这篇文章。
看一个示例程序,就能明白浅拷贝与深拷贝的区别了:
#!/usr/bin/Python3
# -*-coding:utf-8 -*-
import copy
a = [1, 2, 3, ['a', 'b', 'c']]
b = a # 赋值,传对象的引用
c = copy.copy(a) # 浅拷贝
d = copy.deepcopy(a) # 深拷贝
a.append(4)
a[3].append('d')
print(id(a), id(b), id(c), id(d)) # a 与 b 的内存地址相同
print('a = ', a)
print('b = ', b)
print('c = ', c)
print('d = ', d) # [1, 2, 3, ['a', 'b', 'c']]
程序输出如下:
2061915781832 2061915781832 2061932431304 2061932811400
a = [1, 2, 3, ['a', 'b', 'c', 'd'], 4]
b = [1, 2, 3, ['a', 'b', 'c', 'd'], 4]
c = [1, 2, 3, ['a', 'b', 'c', 'd']]
d = [1, 2, 3, ['a', 'b', 'c']]
Python 继承多态理解
- 多态是指对不同类型的变量进行相同的操作,它会根据对象(或类)类型的不同而表现出不同的行为。
- 继承可以拿到父类的所有数据和方法,子类可以重写父类的方法,也可以新增自己特有的方法。
- 先有继承,后有多态,不同类的对象对同一消息会作出不同的相应。
Python 面向对象的原则