python 学习笔记

python基础:

目录

python基础:

数据类型和变量

浮点数

字符串

赋值

浮点数/整数运算

**用法

字符串和编码

使用list和tuple

list:

tuple:

可变与不可变辨析

条件判断:

循环:

for...in循环

while循环

break

continue

使用dict和set

dict

set

函数

调用函数:

定义函数

空函数

返回多个值

小结

函数的参数:

默认参数:

可变参数

关键字参数

命名关键字参数

参数组合

递归函数:

高级特性

切片

迭代

列表生成式

生成器

迭代器

函数式编程:

高阶函数

map/reduce

filter

sorted

返回函数

匿名函数

装饰器

偏函数

模块

使用模块

安装第三方模块

面向对象编程

类和实例

访问限制

继承和多态

继承

多态

获取对象信息

type()函数

isinstance()

dir()函数

实例属性和类属性


数据类型和变量

浮点数

      可以用数学写:1.23,-2.34等,也可以用科学计数法表示:一个浮点数的小数点位置是可变的,例如1.23*10^9 和12.3*10^8 因为太小了,看成完全相等的。对于很大或很小的浮点数,就必须用科学计数法表示,把10用e替代,1.23*10^9就是1.23e9。

字符串

    以单引号 '或双引号 "括起来的任意文本。当用到 ' 或者 " 时用转义字符 \ 来表示。

  • \n 表示换行
  • \t 制表符
  • \\ 表示的就是 \
>>> print('I\'m ok.')
I'm ok.
>>> print('I\'m learning\nPython.')
I'm learning
Python.
>>> print('\\\n\\')
\
\

如果字符串里面有很多字符都需要转义,可以用 r' ' 表示

>>> print('\\\t\\')
\       \
>>> print(r'\\\t\\')
\\\t\\

如果字符串内部有很多换行,用'''...'''的格式表示多行内容

>>> print('''line1
... line2
... line3''')
line1
line2
line3

多行字符串'''...'''还可以在前面加上r使用:

>>>print(r'''hello,\n
world''')
hello,\n
world

>>> _

赋值

等号=是赋值语句,可以把任意数据类型赋值给变量,同一个变量可以反复赋值,而且可以是不同类型的变量,例如:

a = 123 # a是整数
print(a)
a = 'ABC' # a变为字符串
print(a)

#结果:
123
ABC

在赋值时Python解释器主要工作:1.在内存创建字符串 2.在内存中创建变量,并指向该字符串。

a = 'ABC'
b = a
a = 'XYZ'
print(b)

#结果:
ABC

对变量赋值 b=a 是把变量b指向变量a指向的真正的对象('ABC')。随后对变量a的赋值不影响变量b的指向

浮点数/整数运算

  • 整数除法(地板除): 10 // 3 = 3

  • 取余: 10 % 3 = 1

  • 其余正常都是按浮点运算进行

  • 如 9/ 3 = 3.0,结果是按浮点数运算

**用法

  • 放在变量前,表示多少次方,如 2.0 ** 3.0,结果为8;功能类似于C语言中的 ^符号

  • 放在参数前,表示入参为字典,如 dealit(x, **args),第二个输入参数需为字典

字符串和编码

bytes类型的数据用带b前缀的单引号或双引号表示:

x = b'ABC'

以Unicode表示的str通过encode()方法可以编码为指定的bytes:

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

bytes变为str,就需要用decode()方法:

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

输入输出

在Python中,采用的格式化方式和C语言是一致的,用%实现:

>>> 'Hello, %s' % 'world'
'Hello, world'
>>> 'Hi, %s, you have $%d.' % ('Michael', 1000000)
'Hi, Michael, you have $1000000.'

常见的占位符有:

占位符替换内容
%d整数
%f浮点数
%s字符串
%x十六进制整数
%%%

另一种格式化字符串的方法是使用字符串的format()方法:

>>> 'Hello, {0}, 成绩提升了 {1:.1f}%'.format('小明', 17.125)
'Hello, 小明, 成绩提升了 17.1%'

最后一种格式化字符串的方法是使用以f开头的字符串,称之为f-string,它和普通字符串不同之处在于,字符串如果包含{xxx},就会以对应的变量替换:

>>> r = 2.5
>>> s = 3.14 * r ** 2
>>> print(f'The area of a circle with radius {r} is {s:.2f}')
The area of a circle with radius 2.5 is 19.62

使用list和tuple

list:

是个数组列表,里面可以存浮点、整数、字符串等不同类型

list可以内部再嵌套list类型,实现多维数组

定义方式为num = []

初始化举例:classmates = ['Michael', 'Bob', 'Tracy']

>>> classmates = ['Michael', 'Bob', 'Tracy']
>>> classmates
['Michael', 'Bob', 'Tracy']
>>> classmates[0]
'Michael'
>>> classmates[1]
'Bob'
>>> classmates[2]
'Tracy'
>>> classmates[-1]
'Tracy'

常用操作:

  • 取长度,len(),不仅限于list,可以针对任意对象
  • 引用,xx[0],     xx[-1]--指的是最后一个元素
  • 追加,xx.append(‘’),往列表末尾插入对象,注意一次只能一个元素
  • 插入,xx.insert(i, ‘’),往指定位置插入对象
  • 删除末尾元素,xx.pop()
  • 删除指定,xx.pop(i),删除指定位置i处的对象
  • 排序,xx.sort(),默认按字典序或数值升序

tuple:

tuple也是有序列表叫元组,一旦初始化就不能修改,因此没有增删改操作,只有查操作

定义一个空的tuple:

>>> t = ()
>>> t
()

定义一个只有1个元素的tuple:

>>> t = (1,)
>>> t
(1,)

#注意下面错误的定义,这样定义定义的不是一个元组而是一个数
>>> t = (1)
>>> t
1

初始化举例:classmates = ('Michael', 'Bob', 'Tracy')

可变与不可变辨析

>>> t = ('a', 'b', ['A', 'B'])
>>> t[2][0] = 'X'
>>> t[2][1] = 'Y'
>>> t
('a', 'b', ['X', 'Y'])

表面上看,tuple的元素确实变了,但其实变的不是tuple的元素,而是list的元素。tuple一开始指向的list并没有改成别的list,所以,tuple所谓的“不变”是说,tuple的每个元素,指向永远不变。即指向'a',就不能改成指向'b',指向一个list,就不能改成指向其他对象,但指向的这个list本身是可变的!

>>> a = 'abc'
>>> a.replace('a', 'A')
'Abc'
>>> a
'abc'

replace方法会创建返回一个副本,a本身指向的内容不会变;

>>> a = 'abc'
>>> b = a.replace('a', 'A')
>>> b
'Abc'
>>> a
'abc'

条件判断:

if语句的完整形式就是:

if <条件判断1>:
    <执行1>
elif <条件判断2>:
    <执行2>
elif <条件判断3>:
    <执行3>
else:
    <执行4>
  • input()返回的数据类型是str。
  • int()函数:强制转换为int类型。
s = input('birth: ')
birth = int(s)
if birth < 2000:
    print('00前')
else:
    print('00后')

循环:

for...in循环

names = ['Michael', 'Bob', 'Tracy']
for name in names:
    print(name)

while循环

#计算100以内所有奇数之和
sum = 0
n = 99
while n > 0:
    sum = sum + n
    n = n - 2
print(sum)

break

break:提前结束循环。

n = 1
while n <= 100:
    if n > 10: # 当n = 11时,条件满足,执行break语句
        break # break语句会结束当前循环
    print(n)
    n = n + 1
print('END')

continue

continue:跳过当前的这次循环,直接开始下一次循环:

n = 0
while n < 10:
    n = n + 1
    if n % 2 == 0: # 如果n是偶数,执行continue语句
        continue # continue语句会直接继续下一轮循环,后续的print()语句不会执行
    print(n)
  • range(i):生成0到 i-1 的整数序列
  • list(range(i)):将该整数序列转为list

使用dict和set

dict

数据字典,key-value存储方式,key算出value的存放位置

# key :人名,vaule:成绩
>>> d = {'Michael': 95, 'Bob': 75, 'Tracy': 85}  
>>> d['Michael']
95

判断key存不存在的两种方法:

一是通过in判断key是否存在:

>>> 'Thomas' in d
False

二是通过dict提供的get()方法,如果key不存在,可以返回None,或者自己指定的value:

注意:返回None的时候Python的交互环境不显示结果。

>>> d.get('Thomas')
>>> d.get('Thomas', -1)
-1

d.pop(key):删除一个key,对应的value也会从dict中删除

set

set和dict类似,也是一组key的集合,但不存储value。由于key不能重复,所以,在set中,没有重复的key。

创建set,需要提供一个list作为输入集合:

>>> s = set([1, 2, 3])
>>> s
{1, 2, 3}

重复元素在set中自动被过滤:

>>> s = set([1, 1, 2, 2, 3, 3])
>>> s
{1, 2, 3}

s.add(key):添加元素到set中,t添加相同的元素不会有效果,因为set中没有重复的key值。

s.remove(key):删除元素。

set可以看成数学意义上的无序和无重复元素的集合,因此,两个set可以做数学意义上的交集、并集等操作:

>>> s1 = set([1, 2, 3])
>>> s2 = set([2, 3, 4])
>>> s1 & s2
{2, 3}
>>> s1 | s2
{1, 2, 3, 4}

函数

调用函数:

  • abs():求绝对值函数
  • max():有任意个参数,返回最大的
  • int(‘str’):字符串转成整数
  • float(‘str’):字符串转成浮点值
  • str(num):将数字转为字符串
  • bool(elment):判断元素(可以为数字、字符串、list等类型)的真假
  • hex(num):将十进制整数用十六进制输出
  • isinstance(x, (int, float)):判断x元素的类型是不是int 或float,若果是,则为true,否则为false
  • str.lower():将字符串str转为小写
  • str.upper():将字符串str转为大写

定义函数

定义一个函数要使用def语句,依次写出函数名、括号、括号中的参数和冒号:,然后,在缩进块中编写函数体,函数的返回值用return语句返回。

自定义一个求绝对值的my_abs函数:

def my_abs(x):
    if x >= 0:
        return x
    else:
        return -x

空函数

定义一个什么事也不做的空函数,可以用pass语句:

def nop():
    pass

实际上pass可以用来作为占位符,比如现在还没想好怎么写函数的代码,就可以先放一个pass,让代码能运行起来。

返回多个值

Python的函数返回多值其实就是返回一个tuple。

import math

def move(x, y, step, angle=0):
    nx = x + step * math.cos(angle)
    ny = y - step * math.sin(angle)
    return nx, ny
>>> x, y = move(100, 100, 60, math.pi / 6)
>>> print(x, y)
151.96152422706632 70.0

看似获得了两个返回值,其实是单一值是个tuple。

>>> r = move(100, 100, 60, math.pi / 6)
>>> print(r)
(151.96152422706632, 70.0)

在语法上,返回一个tuple可以省略括号,而多个变量可以同时接收一个tuple,按位置赋给对应的值,所以,Python的函数返回多值其实就是返回一个tuple,但写起来更方便。

小结

  • 定义函数时,需要确定函数名和参数个数;
  • 如果有必要,可以先对参数的数据类型做检查;
  • 函数体内部可以用return随时返回函数结果;
  • 函数执行完毕也没有return语句时,自动return None
  • 函数可以同时返回多个值,但其实就是一个tuple。

函数的参数:

默认参数:

可以灵活的控制函数的入参,参数不够时,按默认配参输入

def power(x, n=2):
    s = 1
    while n > 0:
        n = n - 1
        s = s * x
    return s

调用可以写:power(5),相当于调用power(5, 2),当n>2时,必须明确地传入n,比如power(5, 3)

设置默认参数时的注意事项:

  • 必选参数在前,默认参数在后。
  • 当函数有多个参数时,把变化大的参数放前面,变化小的参数放后面。变化小的参数就可以作为默认参数。
  • 对于确定的参数输入,优先赋值给必选参数。
  • 默认参数必须指向不变对象!

可变参数

本质是传入的参数是tuple

def calc(*numbers):
    sum = 0
    for n in numbers:
        sum = sum + n * n
    return sum

*numbers,表示将tuple指针也引用成每个元素

如果已经有一个list或者tuple,要调用一个可变参数:

方法一:

>>> nums = [1, 2, 3]
>>> calc(nums[0], nums[1], nums[2])
14

方法二:

Python允许你在list或tuple前面加一个*号,把list或tuple的元素变成可变参数传进去:

>>> nums = [1, 2, 3]
>>> calc(*nums)
14

*nums表示把nums这个list的所有元素作为可变参数传进去。

关键字参数

关键字参数允许你传入0个或任意个含参数名的参数,这些关键字参数在函数内部自动组装为一个dict,即有key-value映射。

实例:

def person(name, age, **kw):
    print('name:', name, 'age:', age, 'other:', kw)
>>> extra = {'city': 'Beijing', 'job': 'Engineer'}
>>> person('Jack', 24, **extra)
name: Jack age: 24 other: {'city': 'Beijing', 'job': 'Engineer'}

**extra表示把extra这个dict的所有key-value用关键字参数传入到函数的**kw参数,kw将获得一个dict,注意kw获得的dict是extra的一份拷贝,对kw的改动不会影响到函数外的extra

命名关键字参数

如果要限制关键字参数的名字,就可以用命名关键字参数。,例如,只接收cityjob作为关键字参数:

def person(name, age, *, city, job):
    print(name, age, city, job)

和关键字参数**kw不同,命名关键字参数需要一个特殊分隔符**后面的参数被视为命名关键字参数。*后面的参数可以有可以没有,如果有的话必须说明是city还是job。

def person(name, age, *, city='Beijing', job):
    print(name, age, city, job)
>>> person('Jack', 24, job='Engineer')
Jack 24 Beijing Engineer

使用命名关键字参数时,要特别注意,如果没有可变参数,就必须加一个*作为特殊分隔符。如果缺少*,Python解释器将无法识别位置参数和命名关键字参数。

def person(name, age, city, job):
    # 缺少 *,city和job被视为位置参数
    pass
def person(name, age, *args, city, job): ## 参数可变
    print(name, age, args, city, job)

参数组合

       在Python中定义函数,可以用必选参数、默认参数、可变参数、关键字参数和命名关键字参数,这5种参数都可以组合使用。但是请注意,参数定义的顺序必须是:必选参数、默认参数、可变参数、命名关键字参数和关键字参数。

def f1(a, b, c=0, *args, **kw):
    print('a =', a, 'b =', b, 'c =', c, 'args =', args, 'kw =', kw)

def f2(a, b, c=0, *, d, **kw):
    print('a =', a, 'b =', b, 'c =', c, 'd =', d, 'kw =', kw)

>>> f1(1, 2)
a = 1 b = 2 c = 0 args = () kw = {}
>>> f1(1, 2, c=3)
a = 1 b = 2 c = 3 args = () kw = {}
>>> f1(1, 2, 3, 'a', 'b')
a = 1 b = 2 c = 3 args = ('a', 'b') kw = {}
>>> f1(1, 2, 3, 'a', 'b', x=99)
a = 1 b = 2 c = 3 args = ('a', 'b') kw = {'x': 99}
>>> f2(1, 2, d=99, ext=None)
a = 1 b = 2 c = 0 d = 99 kw = {'ext': None}

*args是可变参数,args接收的是一个tuple;

**kw是关键字参数,kw接收的是一个dict。

以及调用函数时如何传入可变参数和关键字参数的语法:

可变参数既可以直接传入:func(1, 2, 3),又可以先组装list或tuple,再通过*args传入:func(*(1, 2, 3))

关键字参数既可以直接传入:func(a=1, b=2),又可以先组装dict,再通过**kw传入:func(**{'a': 1, 'b': 2})

对于任意函数,都可以通过类似func(*args, **kw)的形式调用它,无论它的参数是如何定义的。

>>> args = (1, 2, 3, 4)
>>> kw = {'d': 99, 'x': '#'}
>>> f1(*args, **kw)
a = 1 b = 2 c = 3 args = (4,) kw = {'d': 99, 'x': '#'}
>>> args = (1, 2, 3)
>>> kw = {'d': 88, 'x': '#'}
>>> f2(*args, **kw)
a = 1 b = 2 c = 3 d = 88 kw = {'x': '#'}

递归函数:

在函数内部,可以调用其他函数。如果一个函数在内部调用自身本身,这个函数就是递归函数。

函数调用是通过栈(stack)这种数据结构实现的,每当进入一个函数调用,栈就会加一层栈帧,每当函数返回,栈就会减一层栈帧。

高级特性

切片

在list/tuple中访问任意片段或间隔规律的元素,注意访问下标是左闭右开[left, right)

实例:

#先创建一个0-99的数列
>>> L = list(range(100))
>>> L
[0, 1, 2, 3, ..., 99]

#通过切片轻松取出某一段数列。比如前10个数:
>>> L[:10]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

后10个数:
>>> L[-10:]
[90, 91, 92, 93, 94, 95, 96, 97, 98, 99]

#前11-20个数:
>>> L[10:20]
[10, 11, 12, 13, 14, 15, 16, 17, 18, 19]

#前10个数,每两个取一个:
>>> L[:10:2]
[0, 2, 4, 6, 8]

#所有数,每5个取一个:
>>> L[::5]
[0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80, 85, 90, 95]

#只写[:]就可以原样复制一个list:
>>> L[:]
[0, 1, 2, 3, ..., 99]

字符串'xxx'也可以看成是一种list,每个元素就是一个字符。因此,字符串也可以用切片操作,只是操作结果仍是字符串:

>>> 'ABCDEFG'[:3]
'ABC'
>>> 'ABCDEFG'[::2]
'ACEG'

小结:

s[:],显示所有元素

s[-1:],从-1到0,即显示最后一个元素

s[:10],从0开始到9,即显示前10个元素,下标为10的不显示

s[2:10:3],从下标2到10,每间隔3个,取一个元素

s[-10:-1],从-10个元素,显示至倒数第二个元素,-1是截止,且为开

迭代

定义:

如果给定一个listtuple,我们可以通过for循环来遍历这个listtuple,这种遍历我们称为迭代(Iteration)。

>>> d = {'a': 1, 'b': 2, 'c': 3}
>>> for key in d:
...     print(key)
...
a
b
c

字符串也是可迭代对象:

>>> for ch in 'ABC':
...     print(ch)
...
A
B
C

如何判断一个对象是可迭代对象呢?方法是通过collections.abc模块的Iterable类型判断:

>>> from collections.abc import Iterable
>>> isinstance('abc', Iterable) # str是否可迭代
True
>>> isinstance([1,2,3], Iterable) # list是否可迭代
True
>>> isinstance(123, Iterable) # 整数是否可迭代
False

列表生成式

列表生成式即List Comprehensions,是Python内置的非常简单却强大的可以用来创建list的生成式。

写列表生成式时,把要生成的元素x * x放到前面,后面跟for循环,就可以把list创建出来:

>>> [x * x for x in range(1, 11)]
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

增加判断如果只有if将if放到后面,如果有if和else时将if else放到前面:

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

>>> [x if x % 2 == 0 else -x for x in range(1, 11)]
[-1, 2, -3, 4, -5, 6, -7, 8, -9, 10]

在一个列表生成式中,for前面的if ... else是表达式,而for后面的if是过滤条件,不能带else

可以使用两层循环,可以生成全排列:

>>> [m + n for m in 'ABC' for n in 'XYZ']
['AX', 'AY', 'AZ', 'BX', 'BY', 'BZ', 'CX', 'CY', 'CZ']

生成器

定义:一边循环一边计算的机制,称为生成器:generator。生成器的本质是生成一个tuple。

要创建一个generator,有很多种方法。第一种方法很简单,只要把一个列表生成式的[]改成(),就创建了一个generator:

>>> L = [x * x for x in range(10)]
>>> L
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
>>> g = (x * x for x in range(10))
#如果要一个一个打印出来,可以通过next()函数获得generator的下一个返回值:
>>> next(g)
0
>>> next(g)
1
>>> next(g)
4
>>> next(g)
9
#也可以用for循环:
>>> g = (x * x for x in range(10))
>>> for n in g:
...     print(n)
... 
0
1
4
9
16
25
36
49
64
81

方法二: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'

如果一个函数定义中包含yield关键字,那么这个函数就不再是一个普通函数,而是一个generator函数,调用一个generator函数将返回一个generator。

generator函数和普通函数的执行流程不一样。普通函数是顺序执行,遇到return语句或者最后一行函数语句就返回。而变成generator的函数,在每次调用next()的时候执行,遇到yield语句返回,再次执行时从上次返回的yield语句处继续执行。

迭代器

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

一类是集合数据类型,如listtupledictsetstr等;

一类是generator,包括生成器和带yield的generator function。

这些可以直接作用于for循环的对象统称为可迭代对象:Iterable

可以被next()函数调用并不断返回下一个值的对象称为迭代器:Iterator。

可以使用isinstance()判断一个对象是否是Iterator对象:

>>> from collections.abc import Iterator
>>> isinstance((x for x in range(10)), Iterator)
True
>>> isinstance([], Iterator)
False
>>> isinstance({}, Iterator)
False
>>> isinstance('abc', Iterator)
False

生成器都是Iterator对象,但listdictstr虽然是Iterable,却不是Iterator

listdictstrIterable变成Iterator可以使用iter()函数:

>>> isinstance(iter([]), Iterator)
True
>>> isinstance(iter('abc'), Iterator)
True

小结:

凡是可作用于for循环的对象都是Iterable类型;

凡是可作用于next()函数的对象都是Iterator类型,它们表示一个惰性计算的序列;

集合数据类型如listdictstr等是Iterable但不是Iterator,不过可以通过iter()函数获得一个Iterator对象。

函数式编程:

高阶函数

变量可以指向函数

# 求-10的绝对值
print(abs(-10))  # 10
print(abs)  # <built-in function abs>
# abs()对函数的调用,abs是函数本身
f = abs  # f变量也是指向abs所指向的函数
# 可以用变量f来调用函数
print(f(-10))
# 结论:变量 f 指向了abs函数本身

函数名也是变量

函数名就是指向函数的变量。

# 函数名也是变量
abs = 10
print(abs)      #10
print(abs(-10)) # 报错

报错的原因就是把abs这个函数名当成了变量,指向10,此时的abs就不再指向绝对值函数。

一个函数就可以接收另一个函数作为参数,这种函数就是高阶函数

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

print(add(-5, 6, abs)) # 11

map/reduce

map()函数接收两个参数,第一个是函数,,后面的参数是一个或多个Iterablemap将传入的函数依次作用到序列的每个元素,并把结果作为新的Iterator返回。

map函数的语法:

map(function, iterable, …)

#求f(x)=x^2 作用在list[1,2,3,4,5,6,7,8,9]
a=[1,2,3,4,5,6,7,8,9]
def f(x):
    return x*x
rs=[]
for i in a:
    rs.append(f(i))
print(rs)  #[1, 4, 9, 16, 25, 36, 49, 64, 81]

#使用map实现:

rs1=map(f,a)  #返回的rs1是一个可迭代对象
#判断是否是可迭代对象(可以省略,只是初学时的判断)
from collections.abc import Iterator
print('判断是否为可迭代的',isinstance(rs1,Iterator))   #true

print(list(rs1))  #[1, 4, 9, 16, 25, 36, 49, 64, 81]  

利用map将列表中的每个元素转换为字符串::

a=[1,2,3,4,5,6,7,8,9]
s=map(str,a)
print(list(s))

map传递两个列表:

a=[1,2,3,4]
b=[10,20,30]
def f(x,y):
    return x+y
L=map(f,a,b)
print(list(L))

reduce:

reduce把一个函数作用在一个序列[x1,x2,x3,x4...]上,这个函数必须接收两个参数,reduce把结果继续和序列的下一个元素做累积计算,其效果是:

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

对一个序列求和:

from functools import reduce
def add(x,y):
    return x+y
sum=reduce(add,[1,2,3,4,5])
print(sum)    #15

 把序列[1,2,3,4]变为整数1234:

def zh(x,y):
    return x*10+y
print(reduce(zh,[1,2,3,4]))   #1234

写出把str转换为int:

def fn(x,y):
    return x*10+y
def char2num(s):
    sz={'0':0,'1':1,'2':2,'3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9}
    return sz[s]
print(reduce(fn,map(char2num,'12345')))  #12345

还可以用lambda函数进一步简化成:

from functools import reduce
def char2num(s):
    sz={'0':0,'1':1,'2':2,'3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9}
    return sz[s]
def str2int(s):
    return reduce(lambda x, y: x * 10 + y, map(char2num, s))
print(str2int('12345'))

filter

filter()函数用于过滤序列。

map()类似,filter()也接收一个函数和一个序列。和map()不同的是,filter()把传入的函数依次作用于每个元素,然后根据返回值是True还是False决定保留还是丢弃该元素。

在一个list中,删掉偶数,只保留奇数:

def is_odd(l):
    return l%2==1
print(list(filter(is_odd,[1,2,3,4,5,6,7,8,9])))

把一个序列中的空字符串删掉:

def no_empty(s):
    return s and s.strip()
print(list(filter(no_empty,['A', '', 'B', None, 'C', '  '])))

需要注意的是:''和None都是空字符串但是'    '是一个字符串,字符串的内容为空格。

sorted

对数大小进行比较:

print(sorted([23,32,4,56,-20]))
#输出结果:
[-20, 4, 23, 32, 56]

逆序排序,给sorted添加reverse参数:

print(sorted([23,32,4,56,-20],reverse=True))
#输出结果:
[56, 32, 23, 4, -20]

对字符串 利用ascll码 A:65 a:97

print(sorted(['ABC','D','C','d','abc']))

sorted是高阶函数,他还可以接收一个key函数来实现自定义的排序
对数值的列表按绝对值的大小进行排序

print(sorted([23,32,4,56,-20],key=abs))
#输出结果:
[4, -20, 23, 32, 56]

对字符串列表,忽略大小写

print(sorted(['ABC','D','C','d','abc'],reverse=True,key=str.lower))
print(sorted(['ABC','d','C','D','abc'],key=str.lower))
print(sorted(['ABC','D','C','d','abc'],key=str.lower))
#输出结果:
['D', 'd', 'C', 'ABC', 'abc']
['ABC', 'abc', 'C', 'd', 'D']
['ABC', 'abc', 'C', 'D', 'd']

返回函数

高阶函数除了可以接受函数作为参数外,还可以把函数作为结果值返回。

def lazy_sum(*args):
    def sum():      #这是一个局部函数
        ax = 0
        for n in args:
            ax = ax + n
        return ax
    return sum
#当我们调用lazy_sum()时,返回的并不是求和结果,而是求和函数:
f = lazy_sum(1, 3, 5, 7, 9)
print(f)   #<function lazy_sum.<locals>.sum at 0x000002996224A4C0>
#当调用函数f()时才计算求和的结果
print(f())  #25

在函数lazy_sum中又定义了函数sum,并且,内部函数sum可以引用外部函数lazy_sum的参数和局部变量,当lazy_sum返回函数sum时,相关参数和变量都保存在返回的函数中,这种称为“闭包(Closure)。在上面实例中,sum()是一个闭包,因为他引用了lazy_sum的参数args。

如何创建闭包:

a.要有函数的嵌套(外部函数,内部函数)

b.内部函数中使用外部函数的变量

c.外部函数必须有返回值

调用lazy_sum()时,每次调用都会返回一个新的函数,即使传入相同的参数:

f1 = lazy_sum(1, 3, 5, 7, 9)
f2 = lazy_sum(1, 3, 5, 7, 9)
print(f1)   #<function lazy_sum.<locals>.sum at 0x00000160FE15A550>
print(f2)   #<function lazy_sum.<locals>.sum at 0x00000160FE15A5E0>

f1()和f2()的调用结果互不影响。

返回闭包时牢记一点:返回函数不要引用任何循环变量,或者后续会发生变化的变量。

例子:

使用闭包求两点之间的距离:

'''
两个点(x1,y1)(x2,y2)
距离:math.sqrt((x1-x2)**2+(y1-y2)**2)
'''
import math
def juli(x1,y1,x2,y2):
    return math.sqrt((x1-x2)**2+(y1-y2)**2)
x1=float(input("请输入x1:"))
y1=float(input("请输入y1:"))
x2=float(input("请输入x2:"))
y2=float(input("请输入y2:"))
print('点1为:({0},{1}),点二为:({2},{3})'.format(x1,y1,x2,y2))
print("两点之间的距离是:",juli(x1,y1,x2,y2))

#使用闭包:
def wai(x1,y1):
    def nei(x2,y2):
        return math.sqrt((x1-x2)**2+(y1-y2)**2)
    return nei
a=wai(0,0)
b=a(1,1)
print(b)

匿名函数

匿名函数的表达式:lambda:参数:表达式

匿名函数作为map高阶函数的参数 f(x)=x*x

l=map(lambda x:x*x,[1,2,3,4,5])
print(list(l))  #[1, 4, 9, 16, 25]

sorted中使用匿名函数:

class Student:
     def __init__(self,name,age):
         self.name=name
         self.age=age
stu1=Student('zhangsan',21)
stu2=Student('lisi',25)
stu3=Student('wangwu',23)
result_list=sorted([stu1,stu2,stu3],key=lambda x:x.age)
for stu in result_list:
    print('name:',stu.name,'age:',stu.age)
#输出结果:
name zhangsan age 21
name wangwu age 23
name lisi age 25

装饰器

当我们要在其中一个函数中增加新的功能时,但是又不希望修改函数的定义,这种在代码运行期间动态增加功能的方式,称之为“装饰器”(Decorator)。

这篇文章可以很好的理解 Python 装饰器

偏函数

 Python的functools模块提供了很多有用的功能,其中一个就是偏函数(Partial function)。

偏函数是用于对函数固定属性的函数,作用就是把一个函数某些参数固定住(即默认值),返回一个新的函数,调用这个新的函数就很简单。

以int()函数把字符串转换为整数为例:

#base 默认值为10,传入base参数,可以做N进制的转换:
print('将字符串转换为八进制:',int('12345',base=8))  #将字符串转换为八进制: 5349
print('将字符串转换为十六进制:',int('12345',base=16))  #将字符串转换为十六进制: 74565

若需要转换大量的二进制字符串每次转入int(x,base=2)很麻烦,因此可以默认base=2传进去:

def int2(x):
    return int(x,base=2)
print(int2('1010'))   #10
print(int2('101010'))   #42
print(int2('10101010'))    #170

使用偏函数:首先需要导入functools这个包

from functools import partial
int2=partial(int,base=2)  #partial(函数,默认的值)
print(int2('1010'))   #10
print(int2('101010'))   #42
print(int2('10101010'))    #170
print(int2('10101010',base=10))   #10101010

小结:

当函数的参数个数太多,需要简化时,使用functools.partial可以创建一个新的函数,这个新函数可以固定住原函数的部分参数,从而在调用时更简单。

模块

在Python中,一个.py文件就称之为一个模块(Module)。

模块是一组Python代码的集合,可以使用其他模块,也可以被其他模块使用。

创建自己的模块时,要注意:

  • 模块名要遵循Python变量命名规范,不要使用中文、特殊字符;
  • 模块名不要和系统模块名冲突,最好先查看系统是否已存在该模块,检查方法是在Python交互环境执行import abc,若成功则说明系统存在此模块。

使用模块

在使用模块之前首先是导入模块,导入模块之后就有了变量指向该模块,通过变量就可以访问模块的所有功能。导入模块用import 。

作用域:

  • 正常的函数和变量名是公开的(public),可以被直接引用,比如:abcx123PI等;
  • 类似__xxx__这样的变量是特殊变量,可以被直接引用,但是有特殊用途,比如上面的__author____name__就是特殊变量,我们自己的变量一般不要用这种变量名;
  • 类似_xxx__xxx这样的函数或变量就是非公开的(private),不应该被直接引用,比如_abc__abc等;

总结:

  • 在Python中,是通过_前缀来区分public和private函数,另外特殊的变量用两个 __ 来表示,例如__author__,__doc__, __name__等。
  • 私有函数可以加前缀 __,表示不允许外部访问。
  • 使用模块之前应该先导入。

python中sys.argv的用法

安装第三方模块

第三方库都会在Python官方的pypi.python.org网站注册,要安装一个第三方库,必须先知道该库的名称,可以在官网或者pypi上搜索,比如Pillow的名称叫Pillow,因此,安装Pillow的命令就是:

>>>pip install Pillow

安装常用模块:

直接使用Anaconda,这是一个基于Python的数据处理和科学计算平台,它已经内置了许多非常有用的第三方库,我们装上Anaconda,就相当于把数十个第三方模块自动安装好了,非常简单易用。

面向对象编程

类和实例

面向对象最重要的概念就是类(Class)和实例(Instance),类是抽象的模板,而实例是根据类创建出来的一个个具体的“对象”,每个对象都拥有相同的方法,但各自的数据可能不同。

定义类是通过class关键字:

class Student(object):
    pass

类名通常是大写开头的单词,紧接着是object,表示该类是从哪个类继承下来的,如果没有合适的继承类,就使用object类,这是所有类最终都会继承的类。

创建实例是通过类名+()实现的:

class Student(object):
    pass
#创建实例
st1=Student()
print(st1)  #<__main__.Student object at 0x00000240E74C8400>

定义初始化方法:

class Student(object):
    def __init__(self,name,chengji):
        self.name=name
        self.chengji=chengji

st1=Student('wangteng','70')
print(st1.name,st1.chengji)

注意到__init__方法的第一个参数永远是self,表示创建的实例本身,因此,在__init__方法内部,就可以把各种属性绑定到self,因为self就指向创建的实例本身。

有了__init__方法,在创建实例的时候,就不能传入空的参数了,必须传入与__init__方法匹配的参数,但self不需要传,Python解释器自己会把实例变量传进去。

和普通的函数相比,在类中定义的函数只有一点不同,就是第一个参数永远是实例变量self,并且,调用时,不用传递该参数。

数据封装

在类中定义方法,通过在实例上调用方法,就可以直接操作对象内部的数据,不需要知道方法内部的实现细节。

封装输出函数:

class Student(object):
    def __init__(self,name,chengji):
        self.name=name
        self.chengji=chengji

    def print_chengji(self):
        print('%s: %s' % (self.name, self.chengji))
    def get_chengji(self):
        if self.chengji >= 90:
            return 'A'
        elif self.chengji >= 60:
            return 'B'
        else:
            return 'C'

st1=Student('wangteng',70)
st1.print_chengji()
print(st1.name,st1.chengji,st1.get_chengji())

输出结果:
wangteng: 70
wangteng 70 B

和静态语言不同,Python允许对实例变量绑定任何数据,也就是说,对于两个实例变量,虽然它们都是同一个类的不同实例,但拥有的变量名称都可能不同:

class Student(object):
    def __init__(self,name,chengji):
        self.name=name
        self.chengji=chengji

    def print_chengji(self):
        print('%s: %s' % (self.name, self.chengji))
    def get_chengji(self):
        if self.chengji >= 90:
            return 'A'
        elif self.chengji >= 60:
            return 'B'
        else:
            return 'C'

st1=Student('wangteng',70)
st1.print_chengji()
print(st1.name,st1.chengji,st1.get_chengji())

bart = Student('Bart Simpson', 59)
lisa = Student('Lisa Simpson', 87)
bart.age = 8
print(bart.age)
print(lisa.age)

#输出结果:
8
Traceback (most recent call last):
  File "E:/pythonproject/pythonProject/面向对象编程/01类和实例.py", line 24, in <module>
    print(lisa.age)
AttributeError: 'Student' object has no attribute 'age'

小结:

类是创建实例的模板,而实例则是一个一个具体的对象,各个实例拥有的数据都互相独立,互不影响;

方法就是与实例绑定的函数,和普通函数不同,方法可以直接访问实例的数据;

通过在实例上调用方法,我们就直接操作了对象内部的数据,但无需知道方法内部的实现细节。

访问限制

将变量隐藏起来,通过调用get set方法实现对变量的调用:

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))

    # getter/setter 方法
    def get_name(self):
        return self.__name

    def set_name(self, name):
        self.__name = name

    def get_score(self):
        return self.__score

    def set_score(self, score):
        if score >= 0 and score <= 100:
            self.__score = score
        else:
            raise ValueError('illegal score')

st1 = Student('wangteng', 100)
#print(st1.__name) 报错
'''
Traceback (most recent call last):
  File "E:/pythonproject/pythonProject/面向对象编程/02 访问限制.py", line 27, in <module>
    print(st1.__name)
AttributeError: 'Student' object has no attribute '__name'
'''

print('%s: %s' % (st1.get_name(), st1.get_score())) # 打印:wangteng: 100
st1.set_name('wangteng1')
#st1.set_score(150) # 不合法的分数,抛异常
st1.set_score(99)
print('%s: %d' % (st1.get_name(), st1.get_score())) # 打印:wangteng1: 99

继承和多态

继承

子类的定义是:

def 子类名(父类名)

class Animal(object):
    def run(self):
        print('Animal is running...')

class Dog(Animal):
    pass
class Cat(Animal):
    pass

dog = Dog()
dog.run() # Animal is running...

cat = Cat()
cat.run() # Animal is running...

Animal是父类,Dog和Cat是子类,子类继承了父类的所有方法 。子类也可以添加自己的方法。

多态

class Animal(object):
    def run(self):
        print('Animal is running...')

class Dog(Animal):
    def run(self):
        print('Dog is running...')

class Cat(Animal):
    def run(self):
        print('Cat is running...')

class Tortoise(Animal):
    def run(self):
        print('Tortoise is running slowly...')

def run_twice(a):
    a.run()
    a.run()

run_twice(Animal())
run_twice(Dog())
run_twice(Cat())
run_twice(Tortoise())
#输出结果:
Animal is running...
Animal is running...
Dog is running...
Dog is running...
Cat is running...
Cat is running...
Tortoise is running slowly...
Tortoise is running slowly...

对于一个变量,我们只需要知道它是Animal类型,无需确切地知道它的子类型,就可以放心地调用run()方法,而具体调用的run()方法是作用在AnimalDogCat还是Tortoise对象上,由运行时该对象的确切类型决定,这就是多态真正的威力:调用方只管调用,不管细节,而当我们新增一种Animal的子类时,只要确保run()方法编写正确,不用管原来的代码是如何调用的。这就是著名的“开闭”原则:

对扩展开放:允许新增Animal子类;

对修改封闭:不需要修改依赖Animal类型的run_twice()等函数。

动态语言vs静态语言

静态语言(例如Java):如果需要传入Animal类型,则传入的对象必须是Animal类型或者它的子类,否则,将无法调用run()方法。

动态语言(python):则不一定需要传入Animal类型。我们只需要保证传入的对象有一个run()方法就可以了。


class Animal(object):
    def run(self):
        print('Animal is running...')

def run_twice(a):
    a.run()
    a.run()

class Timer(object):
    def run(self):
        print('Start...')

run_twice(Timer())

输出结果:
Start...
Start...

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

获取对象信息

type()函数

用于基本类型数据:

print(type(123))
print(type('123'))
print(type(None))
#输出结果
<class 'int'>
<class 'str'>
<class 'NoneType'>

判断指向函数或类的变量的类型:

print(type(abs))
class Student(object):
    pass
a=Student()
print(type(a))

输出结果:
<class 'builtin_function_or_method'>
<class '__main__.Student'>

判断 type() 函数作用的变量的类型是否相等:

print(type(123)==type(456))
print(type(123)==int)
print(type('123')==type('abc'))
print(type('123')==str)
print(type("123")==type(456))

输出结果:
True
True
True
True
False

判断一个对象是否是函数:

print(type(fn)==types.FunctionType)
print(type(abs)==types.BuiltinFunctionType)
print(type(lambda x: x)==types.LambdaType)
print(type((x for x in range(10)))==types.GeneratorType)  #生成器类型

输出结果:
True
True
True
True

isinstance()

class Animal(object):
    pass

class Dog(Animal):
    pass

class Husky(Dog):
    pass

f = Animal()
a = Dog()
h = Husky()

print(isinstance(f, Animal)) # True
print(isinstance(a, Animal)) # True
print(isinstance(h, Animal)) # True
print(isinstance(a, Husky)) # False

# isinstance 用于基本数据类型
print(isinstance(123, int)) # True
print(isinstance(True, bool)) # True
print(isinstance(3.14, float)) # True
print(isinstance('hello', str)) # True

# isinstance 判断一个变量是否某些类型中的一种
print(isinstance([1, 2, 3], (list, tuple))) # True

type() 不考虑继承关系,isinstance() 考虑继承关系,因此要判断两个类型是否相同一般使用 isinstance()。

dir()函数

dir()函数:获得一个对象的所有属性和方法。返回一个包含字符串的list。

print(dir('abc'))
输出结果:
['__add__', '__class__', '__contains__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mod__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rmod__', '__rmul__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'capitalize', 'casefold', 'center', 'count', 'encode', 'endswith', 'expandtabs', 'find', 'format', 'format_map', 'index', 'isalnum', 'isalpha', 'isascii', 'isdecimal', 'isdigit', 'isidentifier', 'islower', 'isnumeric', 'isprintable', 'isspace', 'istitle', 'isupper', 'join', 'ljust', 'lower', 'lstrip', 'maketrans', 'partition', 'replace', 'rfind', 'rindex', 'rjust', 'rpartition', 'rsplit', 'rstrip', 'split', 'splitlines', 'startswith', 'strip', 'swapcase', 'title', 'translate', 'upper', 'zfill']

获取一个对象的长度, 使用 len() 函数,内部调用对象的 __len__ 函数

print(len('ABC'))
print('ABC'.__len__())
输出结果:
3
3

若定义的类中也想调用len(myObj)也可以自己定义__len__()方法:

class wt(object):
    def __len__(self):
        return 100
wt1=wt()
print(len(wt1))  #100
print(wt1.__len__())   #100

配合getattr()setattr()以及hasattr(),操作一个对象的状态:

class wt(object):
    def __len__(self):
        return 100
wt1=wt()
print(len(wt1))
print(wt1.__len__())

class wtt(object):
    def __init__(self):
        self.x=9
    def power(self):
        return self.x*self.x
wt=wtt()
# 判断有无属性 x?
print(hasattr(wt, 'x')) # True
print(wt.x) # 9
# 判断有无属性 y?
print(hasattr(wt, 'y')) # False
# 设置一个属性 y?
setattr(wt, 'y', 19)
# 再判断有无属性 y?
print(hasattr(wt, 'y')) # True
# 获取属性 y
print(getattr(wt, 'y')) # 19

# 获取属性,如果不存在,返回默认值
print(getattr(wt, 'z', -1)) # -1

# 获取对象的方法
print(hasattr(wt, 'power')) # True
print(getattr(wt, 'power')) # <bound method wtt.power of <__main__.wtt object at 0x00000171F1014E20>>
fn = getattr(wt, 'power')
print(fn) # <bound method wtt.power of <__main__.wtt object at 0x00000171F1014E20>>
print(fn()) # 81

实例属性和类属性

# 实例属性和类属性
class Student(object):
    # name 是类属性,归 Student 类所有
    name = 'Student'
#创建实例
wt = Student()
# 打印name属性,因为实例并没有name属性,所以会继续查找class的name属性
print(wt.name) # Student
# 打印类的name属性
print(Student.name) # Student
# 给实例绑定name属性
wt.name='wangteng'
# 由于实例属性优先级比类属性高,因此,它会屏蔽掉类的name属性
print(wt.name) #wangteng
# 类属性并未消失,用Student.name仍然可以访问
print(Student.name) # Student
#删除实例的name属性
del wt.name
#再次调用wt.name,由于实例的name属性没有找到,类的name属性就显示出来了
print(wt.name)  # Student

类属性作为计数器的例子:

# 为了统计学生人数,可以给Student类增加一个类属性,每创建一个实例,该属性自动增加:
class Student(object):
    count = 0

    def __init__(self, name):
        self.name = name
        Student.count = Student.count + 1

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

小结:

  • 实例属性属于各个实例所有,互不干扰;
  • 类属性属于类所有,所有实例共享一个属性;
  • 不要对实例属性和类属性使用相同的名字,否则将产生难以发现的错误。
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值