语言基础

语言基础-Python基础02

一、类型和计算

数据类型
数字 3.14,1234,
字符串 'adc',"asd"
列表(List) [1,2,[2,4]]
字典 {'food':'spam','taste':'yum'}
元组 (tuple) (1,'spam',2,'U')
文件
集合
其他类型 None 布尔类型
编程单元类型 函数、模块、类


函数

一、函数传值问题

def chagne_number( b ):
b = 1000

b = 1
chagne_number(b)
print( b )

在 Python 中,字符串,整形,浮点型,tuple 是不可更改的对象,而 list , dict 等是可以更改的对象。

例如:

不可更改的类型:变量赋值 a = 1,其实就是生成一个整形对象 1 ,然后变量 a 指向 1,当 a = 1000 其实就是再生成一个整形对象 1000,然后改变 a 的指向,不再指向整形对象 1 ,而是指向 1000,最后 1 会被丢弃

可更改的类型:变量赋值 a = [1,2,3,4,5,6] ,就是生成一个对象 list ,list 里面有 6 个元素,而变量 a 指向 list ,a[2] = 5则是将 list a 的第三个元素值更改,这里跟上面是不同的,并不是将 a 重新指向,而是直接修改 list 中的元素值。

不可更改的类型:类似 c++ 的值传递,如 整数、字符串、元组。如fun(a),传递的只是 a 的值,没有影响 a 对象本身。比如在 fun(a)内部修改 a 的值,只是修改另一个复制的对象,不会影响 a 本身。

可更改的类型:类似 c++ 的引用传递,如 列表,字典。如 fun(a),则是将 a 真正的传过去,修改后 fun 外部的 a 也会受影响

def chagne_number( b ):
print('函数中一开始 b 的值:{}' .format( b ) )
b = 1000
print('函数中 b 赋值后的值:{}' .format( b ) )

b = 1
chagne_number( b )
print( '最后输出 b 的值:{}' .format( b ) )

打印的结果:

函数中一开始 b 的值:1
函数中 b 赋值后的值:1000
最后输出 b 的值:1

当然,如果参数中的是可更改的类型,那么调用了这个函数后,原来的值也会被更改,具体实例如下:

def chagne_list( b ):
print('函数中一开始 b 的值:{}' .format( b ) )
b.append(1000)
print('函数中 b 赋值后的值:{}' .format( b ) )

b = [1,2,3,4,5]
chagne_list( b )
print( '最后输出 b 的值:{}' .format( b ) )
输出的结果:

函数中一开始 b 的值:[1, 2, 3, 4, 5]
函数中 b 赋值后的值:[1, 2, 3, 4, 5, 1000]
最后输出 b 的值:[1, 2, 3, 4, 5, 1000]

二、匿名函数

python 使用 lambda 来创建匿名函数,也就是不再使用 def 语句这样标准的形式定义一个函数。

匿名函数主要有以下特点:

  • lambda 只是一个表达式,函数体比 def 简单很多。
  • lambda 的主体是一个表达式,而不是一个代码块。仅仅能在 lambda 表达式中封装有限的逻辑进去。
  • lambda 函数拥有自己的命名空间,且不能访问自有参数列表之外或全局命名空间里的参数。

基本语法

lambda [arg1 [,arg2,.....argn]]:expression

sum = lambda num1 , num2 : num1 + num2;

print( sum( 1 , 2 ) )

输出的结果:

3

注意:尽管 lambda 表达式允许你定义简单函数,但是它的使用是有限制的。 你只能指定单个表达式,它的值就是最后的返回值。也就是说不能包含其他的语言特性了, 包括多个语句、条件表达式、迭代以及异常处理等等。

匿名函数中,有一个特别需要注意的问题,比如,把上面的例子改一下:

num2 = 100
sum1 = lambda num1 : num1 + num2 ;

num2 = 10000
sum2 = lambda num1 : num1 + num2 ;

print( sum1( 1 ) )
print( sum2( 1 ) )

你会认为输出什么呢?第一个输出是 101,第二个是 10001,结果不是的,输出的结果是这样:

10001
10001

这主要在于 lambda 表达式中的 num2 是一个自由变量,在运行时绑定值,而不是定义时就绑定,这跟函数的默认值参数定义是不同的。所以建议还是遇到这种情况还是使用第一种解法。

迭代器和生成器

一、迭代器

迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。

迭代器只能往前不会后退。

迭代器有两个基本的方法:iter() 和 next(),且字符串,列表或元组对象都可用于创建迭代器,迭代器对象可以使用常规 for 语句进行遍历,也可以使用 next() 函数来遍历。

1、字符创创建迭代器对象
str1 = 'liangdianshui'
iter1 = iter ( str1 )

2、list对象创建迭代器
list1 = [1,2,3,4]
iter2 = iter ( list1 )

3、tuple(元祖) 对象创建迭代器
tuple1 = ( 1,2,3,4 )
iter3 = iter ( tuple1 )

for 循环遍历迭代器对象
for x in iter1 :
print ( x , end = ' ' )

print('\n------------------------')

next() 函数遍历迭代器
while True :
try :
print ( next ( iter3 ) )
except StopIteration :
break

二、list 生成式(列表生成式)

1、创建 list 的方式

list1=list ( range (1,31) )
print(list1)

输出的结果:

[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30]

2、list 生成式的创建

首先,list 生成式的语法为:

[expr for iter_var in iterable]
[expr for iter_var in iterable if cond_expr]

第一种语法:首先迭代 iterable 里所有内容,每一次迭代,都把 iterable 里相应内容放到iter_var 中,再在表达式中应用该 iter_var 的内容,最后用表达式的计算值生成一个列表。

第二种语法:加入了判断语句,只有满足条件的内容才把 iterable 里相应内容放到 iter_var 中,再在表达式中应用该 iter_var 的内容,最后用表达式的计算值生成一个列表。

list1=[x * x for x in range(1, 11)]
print(list1)

输出的结果:

[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

for 循环后面有 if 的形式

list1= [x * x for x in range(1, 11) if x % 2 == 0]
print(list1)

输出的结果:

[4, 16, 36, 64, 100]

for 循环里面也嵌套 for 循环。

list1= [(x+1,y+1) for x in range(3) for y in range(5)]
print(list1)

输出的结果:

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

其实知道了 list 生成式是怎样组合的,就不难理解这个东西了。因为 list 生成式只是把之前学习的知识点进行了组合,换成了一种更简洁的写法而已。

三、生成器

1、为什么需要生成器

通过上面的学习,可以知道列表生成式,我们可以直接创建一个列表。

但是,受到内存限制,列表容量肯定是有限的。而且,创建一个包含 1000 万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。

在 Python 中,这种一边循环一边计算的机制,称为生成器:generator。

在 Python 中,使用了 yield 的函数被称为生成器(generator)。

跟普通函数不同的是,生成器是一个返回迭代器的函数,只能用于迭代操作,更简单点理解生成器就是一个迭代器。

在调用生成器运行的过程中,每次遇到 yield 时函数会暂停并保存当前所有的运行信息,返回 yield 的值。并在下一次执行 next()方法时从当前位置继续运行。

2、生成器的创建

最简单最简单的方法就是把一个列表生成式的 [] 改成 ()
gen= (x * x for x in range(10))
print(gen)
输出的结果:

<generator object at 0x0000000002734A40>

创建 List 和 generator 的区别仅在于最外层的 [] 和 () 。

但是生成器并不真正创建数字列表, 而是返回一个生成器,这个生成器在每次计算出一个条目后,把这个条目“产生” ( yield ) 出来。

3、遍历生成器的元素

gen= (x * x for x in range(10))

for num in gen :
print(num)
gen= (x * x for x in range(10))

for num in gen :
print(num)

4、以函数的形式实现生成器

其实生成器也是一种迭代器,但是你只能对其迭代一次。

而且实际运用中,大多数的生成器都是通过函数来实现的。

def my_function():
for i in range(10):
print ( i )

my_function()

如果我们需要把它变成生成器,我们只需要把 print ( i ) 改为 yield i 就可以了,具体看下修改后的例子:

def my_function():
for i in range(10):
yield i

print(my_function())

输出的结果:

<generator object my_function at 0x0000000002534A40>

但是,这个例子非常不适合使用生成器,发挥不出生成器的特点,生成器的最好的应用应该是:你不想同一时间将所有计算出来的大量结果集分配到内存当中,特别是结果集里还包含循环。因为这样会耗很大的资源。

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

def odd():
print ( 'step 1' )
yield ( 1 )
print ( 'step 2' )
yield ( 3 )
print ( 'step 3' )
yield ( 5 )

o = odd()
print( next( o ) )
print( next( o ) )
print( next( o ) )

输出的结果:

step 1
1
step 2
3
step 3
5

可以看到,odd 不是普通函数,而是 generator,在执行过程中,遇到 yield 就中断,下次又继续执行。执行 3 次 yield 后,已经没有 yield 可以执行了,如果你继续打印 print( next( o ) ) ,就会报错的。所以通常在 generator 函数中都要对错误进行捕获。

留言