欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

python 基础

程序员文章站 2022-03-02 08:11:41
python基础 # 是注释。通过空格进行缩进,当一行语句以 : 结尾时,缩进的语句视为一段代码块。按约定俗成的规范,使用4个空格进行缩进。最后代码就类似这样: 1. 数据类型和变量 1.1. 数据类型 整数:例如:1, -10, 0等。十六进制表示整数方式为:以 0x 为前缀,后面为0-9, a- ......

 python基础

# 是注释。通过空格进行缩进,当一行语句以 : 结尾时,缩进的语句视为一段代码块。按约定俗成的规范,使用4个空格进行缩进。最后代码就类似这样:

# output name
a = 100
if a > 10:
    print("a>10")
else:
    print("a<=10")

1. 数据类型和变量

1.1. 数据类型

整数:例如:1, -10, 0等。十六进制表示整数方式为:以 0x 为前缀,后面为0-9, a-f,例如:0x11af

浮点数:例如:0.12, -0.1。对于很大的浮点数就用科学计数法表示,把10用e表示,例如:1.23 * 10的9次方表示为1.23e9 或 12.3e8

整数和浮点数在计算器存储方式不同,整数运算永远是精确的,但是浮点数可能会出现四舍五入的误差。

1.2. 字符串

字符串:以 单引号'' 或 双引号"" 括起来的文本,例如:'abc', "abc"

是转义符,例如:'I\'m ok' 中\转义字符串内部中的',输出结果为 I'm ok。此外 \n 表示换行符,\t 表示制表符。

为了简化,还可以通过 r'' 表示''内部的字符串不转义。通过'''...'''来表示很多行,示例:

python 基础
>>> print('\转义: ', '\\\t\\')
\转义:  \    \
>>> print('不转义:', r'\\\t\\')
不转义: \\\t\\
>>> print('多行: ', '''line1
... line2
... line3''')
多行:  line1
line2
line3 
python 基础

1.3. 布尔值

布尔值只有 True 和 False 两种,可以通过 and, or, not 分别进行与、或、非运算。例如:

>>> True and True
True
>>> True or Flase
True
>>> not 5 > 1
False

1.4. 空值

空值使用 None 表示,类似null的含义。 

1.5. 变量

变量可以是任意数据类型,变量名必须是 大小写、数字、_ 组成,不能以数字开头。变量通过 = 赋值,例如:a = 123 将变量a赋值为整数123。

python是动态语言,变量的类型是不固定的。而例如Java则是静态语言,变量在定义时必须指定类型,例如:int a = 123;,赋值时候如果类型不匹配, 
则编译时会报错。与静态语言相比,动态语言更灵活。 

1.6. 常量

常量就是不能变的变量。一般习惯上将常量大写。

python中除法有 / 和 //计算结果是浮点数,// 计算结果则为整数。通过 % 来取两个整数相除的余数。例如:

>>> 10 / 3
3.3333333333333335
>>> 10 // 3
3
>>> 10 % 3
1 

2. 字符串和编码

2.1. 字符串

python中的字符串类型为str,在内存中以Unicode表示,一个字符对应若干字节。如果在网络上传输或保存到磁盘,就需要把str转化为字节bytesbytes类型数据用带b前缀的单引号或双引号表示:x = b'abc',注意 abcstr类型,而b'abc'则是bytes类型。

str通过encode()可以将编码为指定的bytes,例如:

python 基础
>>> '中文'.encode('utf-8')
b'\xe4\xb8\xad\xe6\x96\x87'
>>> 'abc'.encode('ascii')
b'abc'
>>> '中文'.encode('ascii')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-1: ordinal not in range(128)
python 基础

英文的str可以用ASCII编码为bytes,中文的str可以用UTF-8编码为bytes,而中文的str无法通过ASCII编码,因为中文超出ASCII的编码范围,故报错。

bytes可以通过decode()方法解码为str,例如:

>>> b'\xe4\xb8\xad\xe6\x96\x87'.decode('utf-8')
'中文'
>>> b'abc'.decode('ascii')
'abc'

通过len()可以计算str的字符数或bytes的字节数。例如:

>>> len('中文')
2
>>> len('中文'.encode('utf-8'))
6

操作字符串时,我们经常会将str和bytes互相转换,为了避免乱码的问题,一般推荐使用utf-8编码方式。 

2.2. 格式化

python中的字符串格式化方式和C语言类似,都是通过%运算符实现。例如:

>>> "Hi, %s, you have %d" % ('peter', 100)
'Hi, peter, you have 100'

有几个占位符,后面就有几个变量,顺序要对应好。常见的占位符有:

  • %s: 字符串
  • %d: 整数
  • %f: 浮点数,%.2f 表示保留2位小数

另一种格式化字符串的方法是使用字符串的format()方法,它会用传入的参数依次替换字符串内的占位符{0}、{1}...,例如:

>>> "Hi, {0}, you have {1:.2f}".format('peter', 3.1415926)
'Hi, peter, you have 3.14' 

3. 基本数据结构

3.1 list

list是内置的数据结构:列表,表示有序的数据集合。例如:books = ['a', 'b', 'c'],books就是一个list。使用的一些方法如下:

python 基础
>>> books = ['a', 'b', 'c']
>>> books
['a', 'b', 'c']
>>> len(books)  # 计算list元素的个数
3
>>> books[0]    # 通过索引访问list中的元素,索引下标从0开始
'a'
>>> books[4]    # 超出list的范围会报IndexError错误,最后一个下标是 len-1
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
IndexError: list index out of range
>>> books[-1]   # 下标-1代表最后一个元素,-2是倒数第二个元素,以此类推
'c'
>>> books[-2]
'b'
>>> books.append(True)  # 往list队尾添加元素
>>> books.insert(1, ['abc', 'def'])  # 往list指定下标位置(1)添加元素
>>> books       # list内的元素可以是不同的类型
['a', ['abc', 'def'], 'b', 'c', True]
>>> books.pop() # 从队尾删除元素
True
>>> books.pop(1) # 从list指定下标位置(index=1)删除元素
['abc', 'def']
>>> books;
['a', 'b', 'c']
>>> books[1] = 'abc' # 可以类似数组,直接替换指定下标位置的元素
>>> list = list(range(5))   # 通过range()函数生成0-4的整数序列,再通过list()函数转换为list
>>> list
[0, 1, 2, 3, 4] 
python 基础

3.2 tuple

tuple是另一种有序数组,但和list不同的是tuple一经初始化就不能再修改,不能使用append()pop()等修改方法。可以和list类似使用books[0]books[-1]正常访问元素。不可变使得代码更安全。使用方法如下:

>>> books = (1,2,['a','b'])  # 通过(... , ...)定义tuple
>>> books
(1, 2, ['a', 'b'])
>>> books[2][1]
'b' 

3.3 dict

dict全称为dictionary,是python的内置字典。使用 key-value 键值对存储,一个key只对应一个value。类似 java 中的 map,使用了哈希表的数据结构,有极快的查找速度。使用方法如下:

python 基础
>>> dict = {'a':100, 'b':200, 'c':300}
>>> dict['c']           # 根据key获取value
300
>>> dict['Adam'] = 400  # 通过key放入value数据
>>> 'Adam' in dict      # 判断key是否在字典中
True
>>> 'adam' in dict
False
>>> dict.get('Adam')    # key如果不存在,则会返回None
400
>>> dict.pop('a')       # 删除一个key-value对
100
python 基础

注意:dict的key是不可变的

3.4 set

set和dict类似,也是一组key的集合,但不存储value。在set中,key不能重复。使用方法如下:

python 基础
>>> set = set([1,2,3])  # 新建set,以list作为输入集合
>>> set.add(1)          # 往set中添加元素,但set中元素不能重复
>>> set.remove(1)       # 从set中移除元素
>>> s1 = set([1,2,3])
>>> s1
{1, 2, 3}
>>> s2 = set([2,3])
>>> s1 & s2             # 求s1和s2两个set的交集
{2, 3}
>>> s1 | s2             # 求s1和s2两个set的并集
{1, 2, 3} 
python 基础

4. 条件和循环

4.1. 条件判断

条件判读通过if, elif, else完成,完成形式如下:

python 基础
if <条件判断1>:
    <执行1>
elif <条件判断2>:
    <执行2>
elif <条件判断3>:
    <执行3>
else:
    <执行4>
python 基础

如果一个if判断为True,则会忽略下面的判断语句 

4.2. 循环

循环方式有两种,一种是for...in循环,依次将list或tuple中的元素迭代出来,计算1-100的和:

sum = 0
for x in range(101):
    sum += x
print(sum)

另一种方式是while循环,只要条件满足while后语句,就一直循环。计算1-100的和:

sum = 0
x = 1
while x <= 100:
    sum += x
    x += 1
print(sum)

可以通过break提前退出while循环,contince提前结束当前循环,进行下次循环。这两个语句通常需要配合if使用 

三、 函数

1. 调用函数

如果想调用一个函数,需要知道这个函数的名称和参数。如abs()求绝对值的函数,只要一个参数,可以通过help(abs)查看该函数的帮助信息。

python 基础
>>> abs(-1)
1
>>> abs(1,2)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: abs() takes exactly one argument (2 given)
>>> abs('abc')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: bad operand type for abs(): 'str'
python 基础

如果传参的个数或类型不正确,会报TypeError错误,并提示错误信息。

函数名就是指向函数对象的引用,可以将函数名赋值给一个变量,相当于给函数起了个"别名":

>>> a = abs     # 将变量a指向函数abs()
>>> a(-1)       # 通过a调用abs()函数
1 

2. 定义函数

定义函数使用def,函数的返回值使用return,例如:

python 基础
def my_abs(x):
    if not isinstance(x, (int, float)):     # 检查 x 的数据类型
        raise TypeError("type error")       # 抛出TypeError异常
    if x >= 0:
        x;
    else:
        return -x;

print(my_abs(-1))  
python 基础

如果没有return值,则会返回None

pass作为占位符,表示什么都不会做。如果没想好怎么写函数中的代码,可以先用pass让代码运行起来。而缺少了pass,代码会报错。

if x >= 0:
    pass;

函数可以返回多个返回值,但其实是返回的单一值:tuple。但写起来方便,可以使用多个变量来接受一个tuple。

python 基础
def test(x):
    return 1,2

x, y = test('a')
print(test('a'))
输出:
(1, 2)
python 基础

3. 函数的参数

函数除了必选参数外,还提供了默认参数,可变参数和关键字参数,使得定义的函数能处理复杂的参数,简化开发者的调用。

1)默认参数:先定义power函数,计算x的n次方:

python 基础
def enroll(name, gender, age=6, city='Beijing'):
    # 第3、4个参数设置默认值
    print('name:', name)
    print('gender:', gender)
    print('age:', age)
    print('city:', city)
    return

print(enroll('a', 'F'))     # 等同于调用enroll('a','F',6,'Beijing')
print(enroll('a', 'F', city='BJ'))  # 当参数不按定义的顺序传递时,需要把参数名写上,此时age还使用默认值
python 基础

设置默认参数有几点需要注意:

  1. 必选参数在前,默认参数在后。否则无法判断参数的值该是哪个
  2. 变化大的参数放前面,变化小的参数放后面。变化小的可以设置为默认参数,好处就是降低了调用函数的难度。
  3. 默认参数必须指向不可变对象

2)可变参数,传入函数的参数个数是可变的,可以是0, 1...个。可变参数是在参数前面加上了*,例如:

python 基础
def cacl(*number):      # number接收的是一个tuple
    for x in number:
        print(x)

cacl()
cacl(1,2,3)         # 函数可以传任意值
nums = [1,2,3]
cacl(*nums)         # nums前加一个*,将list或tuple的值作为可变参数传递
python 基础

3)关键字参数:允许传递0或任意个含参数名的参数,这些关键字参数在函数内部组装成一个dict。关键字参数可以扩展函数的功能。例如:

python 基础
def person(name, age, **kw):
    print('name', name, 'age', age, 'others', kw)

# 可传入任意个关键字参数
person('peter', 10, gender='M', job='Engineer')

# 可先组装dict,然后通过**dict将所有key-value用关键字参数传入函数的**kw,kw获得dict的一份拷贝,对kw的改动不会影响外面的dict
dict = {'gender':'M', 'job':'Engineer'}
person('peter', 10, **dict)
python 基础

4)命名关键字参数: 
函数调用者可以传入任意不受限制的关键字参数,在函数内部可以通过kw检查到底传递了哪些。例如:

def person(name, age, **kw):
    if 'city' in kw:        # 检查是否有city和job参数
        print(kw['city'])
    if 'job' in kw:
        print(kw['job'])
    print(name, age, kw)

如果要限制关键字参数的名字,可以使用命名关键字参数,和关键字参数**kw不同,命名关键字参数需要一个特殊的分隔符**后面的参数被视为命名关键字参数。例如:

def person(name, age, *, city, job):    # 只接收city和job作为关键字参数
    print(name, age, city, job)

# 命名关键字参数必须传入参数名,如果没有传入参数名,则调用会报错
person('name', 18, city='city', job='job')  

4. 递归函数

函数内部可以调用其他函数,如果一个函数在内部调用本身,则这个函数是递归函数。例如:计算n的阶乘用递归方式写出来就是

def func(n):
    if n == 1:
        return 1
    return n * func(n-1) 

四、高级特性

利用python的一些高级特性,可以用1行代码实现很多功能,从而帮助我们提升了开发效率。 

1. 切片

python提供了切片(Slice)操作符,用于取list或tuple的部分元素。

python 基础
>>> L = ['a','b','c','d','e']
>>> L[0:3]              # 取前3个元素,从索引0到索引3(但不包括索引3)的元素
['a', 'b', 'c']
>>> L[:3]               # 如果第一个索引是0,可以省略
['a', 'b', 'c']
>>> L[-2:-1]            # 倒数切片,倒数第2个元素,倒数第一个元素索引是-1
['d']
>>> L[-2:]              # 倒数前2个元素
['d', 'e']
>>> L[0:5:2]            # 取前5个元素,每2个取一次
['a', 'c', 'e']
>>> L[::2]              # 取所有元素,每2个取一次
['a', 'c', 'e']
python 基础

tuple是不可变的一种list,也支持切片操作,操作结果仍是tuple

>>> (1,2,3,4,5)[:3]
(1, 2, 3)

字符串"abcde"也可看做是一个list,每个元素就是一个字符,也可以使用切片操作,其结果仍旧是字符串

>>> 'abcde'[:5:2]
'ace' 

2. 迭代

如果给定一个list或tuple,可以使用for循环来遍历,这种遍历称为迭代(Iteration)。python中的迭代是通过for...in 来完成,不仅可迭代list/tuple。还可迭代其他对象。

python 基础
# 迭代list
>>> l = list(range(10))
>>> for item in l:
...     print(item)

# 迭代dict,由于dict的存储不是像list那样顺序存储,所有迭代结果可能不是按顺序的
>>> d = {'a':1, 'b':2}
>>> for key in d:               # 默认是迭代的key
...     print(key)
...
b
a
>>> for value in d.values():    # 迭代value
...     print(value)
...
2
1
>>> for k,v in d.items():       # 迭代key和value
...     print(k, v)
...
b 2
a 1

# 迭代字符串
>>> for ch in 'abc':
...     print(ch)
...
a
b
c
python 基础

当使用for时,只要作用与一个迭代对象,就可以正常运行,我们不需要关注迭代对象是list还是其他数据类型。可以通过collections模块的Iterable类型判断一个对象是否是可迭代对象:

python 基础
>>> from collections import Iterable
>>> isinstance('abc', Iterable)     # str类型可迭代
True
>>> isinstance(123, Iterable)       # 整数不可迭代
False
>>> dict={'a':1}
>>> isinstance(dict, Iterable)      # dict类型可迭代
True
python 基础

python内置的enumerate函数可以将list变成索引-元素对。这样可以在for中迭代索引和对象本身:

python 基础
>>> l = ['a','b','c','d']
>>> for i,value in enumerate(l):
...     print(i, value)
...
0 a
1 b
2 c
3 d
python 基础

3. 列表生成式

python内置了简单而强大的生成list的方式,通过使用列表生成式可以写出非常简洁的代码。

# 生成 2-10 的list
>>> L=list(range(2,11))
>>> L
[2, 3, 4, 5, 6, 7, 8, 9, 10]

此外,还可以在for...in...后面加上if进行判断。例如:生成3-10中偶数的平方的list:

>>> L=[x * x for x in range(3,11) if x % 2 ==0]
>>> L
[16, 36, 64, 100]

等价于

python 基础
>>> L=[]
>>> for x in range(3,11):
...     if x % 2 == 0:
...         L.append(x * x)
...
>>> L
[16, 36, 64, 100]
python 基础

列表生成式也可以使用两个变量来生成list:

>>> dict={'a':'1', 'b':'2'}
>>> [k+'='+v for k,v in dict.items()]
['b=2', 'a=1'] 

4. 生成器

通过列表生成器,可以直接创建一个列表。但是,假如创建一个包含100万个元素的列表,需要占用大量的内存空间,而我们只需要前几个元素,那么大多数元素占用的内存空间就浪费了。

对于这种情况,python提供了生成器(generator),能一边循环一边计算,从而不需要创建完整的list,节省了内存空间。

第一种创建generator生成器的方式:将[]改为()

python 基础
>>> L = [x * x for x in range(1,10)]    # 创建list
>>> L                                   # 打印list的每一个元素
[1, 4, 9, 16, 25, 36, 49, 64, 81]
>>> g = (x * x for x in range(1,10))    # 创建generator
>>> g
<generator object <genexpr> at 0x1024e0bf8>
>>> next(g)     # 用next()函数获取generator下一个返回值
1
>>> next(g)
4
python 基础

next()会计算g的下一个元素,当没有元素时,会抛出StopIteration异常。generator是可迭代对象,可以使用for循环来代替next()

python 基础
>>> g = (x * x for x in range(1,5))
>>> for n in g:
...     print(n)
...
1
4
9
16
python 基础

第二种创建generator生成器的方式:函数定义中使用yield,将函数变成generator

下面是生成菲波那切数列的函数,除第一个和第二个数之外,后面每个数等于前两个数的和:

python 基础
def fib(max):
    n, a, b = 0, 0, 1
    while n < max:
        print(b)
        a, b = b, a + b
        n = n + 1
    return 'done'

fib(6)      # 输出菲波那切数列前6个数
python 基础

这种逻辑非常类似generator,可以从第一个元素开始,推算后面任意的元素。将print(b)改为yield b,该函数就就变成了generator。generator和普通函数执行流程不一样:

  • 普通函数是顺序执行,遇到return或最后一行函数语句就会返回;
  • 而变为generator的函数,每次调用next()的时候执行,遇到yield中断并返回值,再次调用next()时从上次yield语句处继续执行。
python 基础
>>> def fib(max):
...     n, a, b = 0, 0, 1
...     while n < max:
...         yield b
...         a, b = b, a + b
...         n = n + 1
...     return 'done'
...
>>> g = fib(6)  # 生成generator对象
>>> g
<generator object fib at 0x1024e0bf8>
>>> next(g)     # 调用next()函数不断获取后一个返回值
1
>>> next(g)
1
>>> next(g)
2
>>> next(g)
3
>>> next(g)
5
>>> next(g)
8
>>> next(g)     # 此时 n = max, 没有yield可以执行了,所有调用就报错了
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration: done

>>> for n in fib(6):    # 也可以使用for循环进行generator的迭代
...     print(n) 
python 基础

5. 迭代器

直接作用于for循环的数据类型有以下几种:

  • 集合数据类型:list,tuple,dict,set,str
  • generator:包括生成器和带yield的生成器函数

这些可作用for循环的对象统称为可迭代对象:Iterable。可通过isinstance()判断一个对象是否是Iterable:

>>> isinstance([],Iterable)
True
>>> isinstance((x for x in range(10)),Iterable)
True
>>> isinstance(123,Iterable)
False

生成器不仅可作用于for循环,还可被next()不断调用返回下一个值,直至抛出StopIteration,这类对象成为迭代器:Iterator,也可使用isinstance进行判断:

>>> from collections import Iterator
>>> isinstance([],Iterator)
False
>>> isinstance((x for x in range(10)), Iterator)
True

迭代器Iterator表示的是一个数据流,可将数据流看做是一个有序队列,但不能提前知道队列的长度,只能通过next()函数实时按需计算下一个元素的值。

五、函数式编程

1. 高阶函数

函数名是指向函数的变量,对于函数abs(),可将abs函数名看做变量,它指向可以计算绝对值的函数。另外,函数本身可以赋值给其他变量:

python 基础
>>> abs(-10)        # 调用求绝对值的函数
10
>>> abs             # abs是函数本身
<built-in function abs>
>>> f=abs           # 将函数本身赋值给变量
>>> f
<built-in function abs>
>>> f(-10)          # 通过变量来调用函数。直接调用abs()和调用变量f()相同
10
python 基础

既然变量可以指向函数,函数的参数能接受变量,那么一个函数就可以接收另一个函数作为参数,这种函数就称为高阶函数。函数式编程就是指这种高度抽象的编程范式。例如:

def add(x, y, f):
    print(f(x) + f(y))

add(-5, 6, abs)     # f=abs 作为参数传入, 最后计算abs(-5) + abs(6)

输出:11 

1.1 map/reduce

map()函数接收两个参数,第一个是函数,第二个是Iterable(可迭代对象)。map将函数作用与序列的每一个元素,并作为结果返回Iterator(迭代器)。举例如下:

# 第一个参数str是str()函数,将list中的每个数字转化为str
>>> result = map(str, [1,2,3,4,5])  
>>> isinstance(result, Iterator)
True
>>> list(result)        # 结果是Iterator惰性序列,通过list()函数将其转化为list
['1', '2', '3', '4', '5']

reduce()函数也接收两个参数,第一个是函数,第二个是Iterable。reduce将函数作用在序列上,将结果继续和序列的下一个元素做累积计算。效果就是: 

reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)

举例如下:将[1,3,5,7,9]转化为13579

>>> from functools import reduce
>>> def fn(x, y):
...     return x * 10 + y
...
>>> reduce(fn, [1,3,5,7,9])
13579

map()可以与reduce()配合起来使用:实现将str转化为int,只需要几行代码

>>> DIGITS = {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9}
# str也是一个Iterable
>>> reduce(lambda x, y:x*10 + y, map(lambda x:DIGITS[x], '13579')) 
13579

1.2 filter

filter()也接收一个函数和一个Iterable序列,将函数依次作用与序列中的每个元素,然后保留返回True的元素,丢弃返回False的元素,最终返回Iterator序列。通过help()了解filter的说明:

python 基础
>>> help(filter)

Help on class filter in module builtins:

class filter(object)
 |  filter(function or None, iterable) --> filter object
 |
 |  Return an iterator yielding those items of iterable for which function(item)
 |  is true. If function is None, return the items that are true.
python 基础

filter()主要是实现筛选功能。例如:筛选list中的奇数

>>> def is_odd(n):
...     return n % 2 == 1
...
# filter返回Iterator序列,需要用list()来获取所有结果
>>> list(filter(is_odd, [1,2,3,4,5]))   
[1, 3, 5]

1.3 sorted

sorted()函数可以对Iterable序列进行排序。默认是从小到大的升序,也可指定reverse=True改为从大到小。它还可以接受一个函数key来自定义排序方法:该函数会作用与序列的每一个元素,然后根据key函数返回的结果进行排序。

python 基础
# 对list进行排序
>>> sorted([1, -5, 3])      
[-5, 1, 3]
# 接收key函数实现自定义排序,按绝对值大小排序
>>> sorted([1, -5, 3], key=abs)     
[1, 3, -5]
# 反向排序
>>> sorted([1, -5, 3], key=abs, reverse=True)
[-5, 3, 1]
python 基础

2. 返回函数

高阶函数除了能将函数作为参数,还可将函数作为返回值。例如:

python 基础
>>> def lazy_sum(*args):
...     def sum():
...         s = 0
...         for n in args:
...             s = s + n
...         return s
...     return sum      # 返回求和函数
...
>>> f=lazy_sum(1,2,3,4,5)   # 调用lazy_sum时,返回求和函数而不是求和结果
>>> f
<function lazy_sum.<locals>.sum at 0x101c09ae8>
>>> f()                 # 调用f()时,才会计算求和结果
15
python 基础

闭包:在上面的例子中,lazy_sum函数中定义了函数sum,内部sum函数可以引用外部函数lazy_sum的参数和局部变量(args)。当lazy_sum返回sum时,相关参数和变量(args)都保存在返回的sum函数中,这种成为闭包。返回闭包时候需要注意一点:返回函数不要引用任何循环变量,或者后续会发生变化的变量。

3. 匿名函数

我们在传入函数时,有时候不需要显式的定义函数,直接传入匿名函数更方便。关键字lambda表示匿名函数,例如:lambda x: x * x表示计算x的平方的函数,冒号前面的x表示函数参数,lambda只需要一个表达式,不需要写return,返回值就是表达式的值。

# map第一个参数为传入的函数
>>> list(map(lambda x : x * x, [1,2,3]))
[1, 4, 9] 

4. 装饰器

装饰器:在代码运行期间动态的增加函数功能的方式称为装饰器。例如:有一个now()函数,希望在函数调用的前后打印日志,而又不修改函数的定义,从而增加now()函数的功能。

python 基础
# 定义能打印日志的装饰器,接收一个函数作为参数,并返回一个函数
import functools

def log(func):
    # 将原始函数now的__name__等属性复制到wapper中,否则部分依赖函数签名的代码执行时会出错
    @functools.wraps(func)      
    def wapper(*args, **kw):
        print("call %s" % func.__name__)
        return func(*args, **kw)
    return wapper

# 使用@语法,将装饰器置于函数的定义处
@log
def now():
    print("2018-01-01")

now()   # 调用now函数,不仅运行now本身,也会在now之前打印一行日志

---
call now
2018-01-01
python 基础

将@log放置在now函数定义处,相当于执行了now = log(now),log()是一个装饰器,返回一个函数,所以现在now指向了新的函数wapper()(即在log内定义的函数)。wapper函数的参数定义是*args, **kw,因此wapper可以接收任何参数。 

5. 偏函数

int()能将字符串转化为整数,默认是按10进制进行转化转换,还提供了额外的base参数来指定做N进制的转换。

>>> int('123')
123
>>> int('123', base=8)  # 按8进制进行转换
83

可以通过functional.partial帮我们创建一个偏函数,创建一个新的函数int2(),将int()函数的base参数设置默认值,从而使用int2()函数转化2进制会更方便一些。注意:新的int2函数中base=2是默认值,也可以设置为其他值:

>>> import functools
>>> int2=functools.partial(int, base=2)
>>> int2('100101')
37
>>> int2('100101',base=10)
100101

六、模块

一个.py文件称为一个模块(module),使用模块提升了代码的可维护性,编写完一个模块,可以在其他模块引用。使用模块还能避免函数和变量名冲突,相同名字的函数和变量可以在不同的模块中。如果为了避免模块名冲突,python又按目录来组织模块的方法,称为包(package)

mycompany
├─ __init__.py
├─ abc.py
└─ xyz.py

以上目录存放例子,mycompany是一个顶层包名,abc.py的模块名就是mycompany.abc。每个目下会有一个__init__.py文件,这个文件是必须的,否则python会将它看做是一个普通的目录,而不是一个包,init.py本身就是一个模块,模块名为mycompany。

自己创建模块时需要注意不要和系统自带的模块名冲突,例如,系统自带了sys模块,自己的模块就不能命名为sys.py。

使用python本身内置的模块,只需要import 模块名就可以了。例如:import sys,就导入了sys模块,变量sys就指向该模块,利用sys变量,就可以访问sys模块的所有功能。 

七、面向对象编程

面向对象编程,简称OOP(Object Oriented Programming),是一种程序设计思想,将对象作为程序的基本单元,一个对象包含了数据和操作数据的函数。python中,所有数据类型都可以视为对象,也可以自定义对象,自定义的对象数据类型就是类(Class)的概念。面向对象的三大特点:封装、继承、多态。

1. 类和实例

类(Class)是抽象的模板,而实例(Instance)是根据类创建出来的一个个具体的"对象",每个对象拥有相同的方法,而各自的数据不同。

python 基础
# class关键字定义类,后面object表示Student是从object继承的,
class Student(object):
    
    # 通过定义特殊的__init__方法,在创建类时,将必须绑定的属性强制填写进去
    # __init__第一个参数是self,表示实例本身,在内部将name和score属性绑定到self上
    def __init__(self, name, score):
        self.name = name
        self.score = score

    def print(self):
        print("%s %s" % (self.name, self.score))

# 创建Student实例,必须传入与__init__方法匹配的参数
std = Student("name", 90)   
# Student实例将name和score数据封装起来,通过调用实例的方法,操作对象的内部数据
std.print()     
python 基础

2. 访问限制

上例中,仍可以通过std.name来访问和修改实例的name属性。如果要将name和score内部属性设置为私有变量(private),可以在属性的名称前加两个下划线__,只有内部可以访问,外部不可以访问。如下:

class Student(object):
    def __init__(self, name, score):
        self.__name = name
        self.__score = score
    def print_score(self):
        print("%s, %s" % (self.__name, self.__score))

变量名类似__xxx__,前后都有两个下划线的是特殊变量,特殊变量可以直接访问,不是private的。

3. 继承与多态

定义一个新的class可以继承已有的class,从而获得父类的全部功能。例如:

python 基础
class Animal(object):
    def run(self):
        print('animal run')

# Cat类继承自Animal类
class Cat(Animal):
    # 将父类中的run方法覆写掉
    def run(self):
        print('cat run')
python 基础

Cat类也可以覆写run方法,从而代码调用时,会调用子类的run方法。这样,我们会获得了多态

python 基础
# 该函数接受Animal类型的变量
>>> def run_twice(animal):
...     animal.run()
...     animal.run()
...
>>> run_twice(Animal())     # 当传入Animal对象时
animal run
animal run
>>> run_twice(Cat())        # 当传入Cat对象时
cat run
cat run
python 基础

对于传入的不同的Animal子类,run_twice()函数不需要做任何修改,只需要接受Animal类型就可以了。因为Cat等子类也算是Animal类型,然后可以按照Animal的类型进行操作,可以放心的调用run()方法,而具体的run()方法是作用在Animal、Cat对象上,由运行时该对象的确切类型决定。这就是多态的好处:

  • 对扩展开放:允许新增Animal的子类
  • 对修改封闭:不需要修改依赖Animal类型的run_twice()函数

静态语言 vs 动态语言:

  • 对于静态语言(例如Java):如果需要传入Animal类型,则传入的对象必须是Animal类型或它的子类,否则将无法调用run()
  • 对于动态语言(例如python):则不一定需要传入Animal类型,只需要保证传入的对象有一个run()方法就行。

这就是动态语言的鸭子类型:不像静态语言那样要求严格的继承体系,一个对象"看起来像鸭子,走起路来像鸭子",那它就可以看做是鸭子。

4. 获取对象信息

可以使用type()来判断对象的类型:

python 基础
# 判断基本类型
>>> type(123)
<class 'int'>
>>> type('123')
<class 'str'>

# 如果一个变量指向一个类或函数
>>> type(Cat())
<class '__main__.Cat'>
>>> type(abs)
<class 'builtin_function_or_method'>
python 基础

type返回Class类型,可以比较两个type是否相同:

>>> type(123)==type(456)
True
>>> type('abc')==str
True
>>> type('abc')==type(123)
False

对类的继承关系,type不太好判断,可以使用instance()函数,告诉我们一个对象是否是该类型,或该类型的子类。例如:Cat类继承了Animal:

python 基础
>>> a = Animal()
>>> c = Cat()
>>> isinstance(a, Animal)
True
>>> isinstance(c, Animal)
True
>>> isinstance(a, Cat)
False

# 判断基本类型
>>> isinstance(123, int)
True
>>> isinstance('123', int)
False
python 基础

可以使用dir()获得一个对象的所有属性和方法,配合getattr()setattr()hasattr(),操作一个对象的状态:

python 基础
>>> a = Animal()
>>> dir(a)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'run']
>>> hasattr(a, 'x')         # 是否有'x'属性
False
>>> setattr(a, 'x', 10)     # 设置属性'x'
>>> getattr(a, 'x')         # 获取属性'x'
10

>>> fn = getattr(a, 'run')  # 获取a对象的run方法,赋值到变量fn
>>> fn()                    # fn()与a.run()是一样的
animal run 
python 基础

5. 实例属性和类属性

python是动态语言,根据类创建的实例可以任意绑定属性。给实例绑定属性时通过实例变量或self:

>>> a = Animal()
>>> a.score = 123

如果直接在Animal类本身绑定一个属性,可在class中定义,这种属性时类属性

python 基础
>>> class Animal(object):
...     name = "name"
...
>>> a = Animal()
>>> a.name      # 实例a没有name属性,会查找class的name属性
'name'
>>> Animal.name # 打印类的name属性
'name'
>>> a.name='name_object'    # 给实例a绑定name属性
>>> a.name                  # 实例属性优先级比类属性高,所有屏蔽了类的name属性
'name_object'
python 基础

实例属性归各个实例所有,互不干扰。类属性属于类所有,所有实例共享一个类属性。不要对实例属性和类属性使用相同的名字,否则将发生难以发现的错误

八、面向对象高级编程

1. 使用slots

在程序运行时可以动态给class绑定属性,但如果想限制实例的属性,例如只允许给Student类添加name或age属性,可以在提供定义class时,设置一个特殊的__slots__变量:

python 基础
>>> class Student(object):
...     __slots__=('name','age')    # 通过tuple设置允许绑定的属性明恒
...
>>> a = Student()
>>> a.name="name"       # 绑定属性name
>>> a.other=123         # 绑定属性other,不在__slots__定义中,出现错误
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'Student' object has no attribute 'other'
python 基础

other不在__slots__定义中,所以不能绑定other属性,得到AttributeError错误。__slots__定义的属性仅对当前实例起作用,对继承的子类不起作用。

2. 使用@property

在java中,如果定义了一个属性,一般会实现这个属性的getter和setter方法。在python中,可以通过@property装饰器将方法变成属性调用,这样既能检查参数,又能用属性这样简单的方式来访问变量。

python 基础
>>> 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 integer')
...         if value >

                    
                
(0)
打赏 python 基础 微信扫一扫

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

python 基础
验证码: python 基础