python是一门面向对象的动态语言,得益于其简单,优雅与明确的设计哲学。到现在,python已经运用到人工智能,大数据,网页处理领域。
在python中,变量与对象的概念分别如下
对象:内存中存储数据的实体,有明确的类型,python中一切皆对象,函数也是对象
变量,指向对象的指针,对对象的引用。作为弱类型语言,python中变量是没有类型的,不想C/C++中int,float等等类型。
python中又可以将对象分为可变对象和不可变对象:
不可变对象:对象对应内存中的值不会变,因此如果指向面向对象的变量被改变了,python会重新开辟一块内存,变量再指向这个新的内存,包括int,float,str,tuple。
可变对象:对象对应的内存中的值可以改变,因此变量改变后,该对象也会改变,如list,dict,set等对象。
对于不可变对象,所有指向该对象的变量在内存中共用一个地址,例如:
>>>a=1
>>>b=1
>>>id(a)==id(b) #查看两个变量的内存地址
>True
可以看出所有指向1的地址都一样
如果修改了不可变对象的变量的值,则原对象的其他变量不变,相比之下,如果修改了可变对象的变量,则相当于可变对象被修改了,其他变量也会发生变换。
>>> a=1
>>> b=1
>>> b=2
>>> a #这里ab都为不可变对象,修改一个并不会影响另一个
1
>>> c=[1]
>>> d=c
>>> d.append(2)
>>> c
[1, 2]
这里dc为可变对象,因此相当于将二者绑定在一起,改变d的同时也改变c。
另外,当python的引用计数为0时,python中的内存也会被回收。
python中的变量也存在着深拷贝和浅拷贝。不可变对象不论深/浅拷贝,其地址都是一样的。而可变对象存在几种情况,下面利用list来进行说明:
直接复制:仅仅拷贝了引用,因此前后变量并没有任何隔离,原list改变,拷贝的变量也会改变。
浅拷贝:使用copy()函数,拷贝了list最外围,但list最内围还是引用。
深拷贝:使用deepcopy()函数,内外围均为拷贝,因此前后对象都是拷贝,完全隔离,改变一个对另一个没有影响。
下面我用代码来说明:
>>> a=[1,2,[1,2]]
>>> b=a
>>> c=copy.copy(a) #浅拷贝
>>> d=a[:] #浅拷贝,相当于c
>>> e=copy.deepcopy(a) #深拷贝,完全隔离
>>> a.append(3) #在a的结尾追加
>>> a[2].append(3) #看那些变量受到影响
>>> a,b #打印各个变量
([1, 2, [1, 2, 3], 3], [1, 2, [1, 2, 3], 3])
>>> c
[1, 2, [1, 2, 3]]
>>> d
[1, 2, [1, 2, 3]]
>>> e
[1, 2, [1, 2]]
作用域
python程序创建,访问,改变一个变量时,都是在一个保存该变量的空间内进行的,这个空间称为命名空间,也叫作用域。python的作用域是静态的,变量被赋值,创建的位置决定了其被访问的范围,即变量的作用域由其所在的位置所决定。
>>>a=1 #a为全局变量
>def local(): #local也是局部变量
> b=2 #b为局部变量
在python中,使用一个变量并不需要先声明这个变量,但是在真正使用这个变量时,它必须被绑定到某个内存对象(被定义,被赋值)中。这种变量名的绑定将在当前作用域引入新的变量,同时屏蔽外层域中的同名变量。
>>> a=1
>>> def local():
... a=2
...
>>> local()
>>> print(a)
1
这样是不可以改变a的值的。想要实现局部修改全局,可以使用两种办法,增加global关键字,或者使用list或dict等可改变对象的内置函数。
>>> a=1
>>> b=[1]
>>> def local():
... global a
... a=2
... b.append(3)
...
>>> print(a)
1
>>> local()
>>> print(a)
2
>>> print(b)
[1, 3]
可见调用local后改变了a,b的值。
另外,python的作用域从内到外,可分为local(局部),enclosed(嵌套),global(全局),built-in(内置),而且变量的搜索遵循LEGB原则,local,enclosed,global,built-in,如果搜索不到则会报错。
高阶函数:
在编程语言中,高阶函数是可以接受函数作为输入和输出的函数,对于python来说,函数是一种对象,既可以赋值给变量,添加到集合中,传参到函数中,也可以作为函数的返回值。下面介绍几种常见的高阶函数map(),reduce(),filter(),sorted()这四种函数。
首先,python中的变量可以指向函数:
>>> f=abs
>>> f(-1)
1
map()函数可以将一个函数映射到可迭代的序列中,即映射到一个有顺序的集合中,并返回输出的序列:
>>> list(map(f,[1,2,3,4]))
[1, 4, 9, 16]
>>> list(map(str,range(4)))
['0', '1', '2', '3']
因为在python3中,map返回的不是一个序列了,而是一个迭代器,因此要利用list将其列出来。
reduce()函数是先对前两个元素操作,再与第三个元素操作,例如
reduce(f,[a,b,c])=f(f(a,b),c)
而且需要从functools引入reduce函数:
>>> from functools import reduce
>>> def f(x,y):return 10*x+y
...
>>> reduce(f,[1,2,3,4])
1234
filter()函数的作用主要是通过输入序列以及条件对其进行过滤。并返回一个可迭代序列,例如:
>>> def is_odd(x):return x%2==0
>>> list(filter(is_odd,[1,2,3,4,5,6]))
[2, 4, 6]
这样就可以得到过滤后的值了。
sorted可以完成对可迭代序列的排序,与系统本身自带的sort函数不同,这里的sorted返回的是一个新的列表,而且可以通过传入关键字key来指定排序的标准,参数reverse表示是反向。
>>> sorted([3,5,-66,55,-101],key=abs,reverse=True)
[-101, -66, 55, 5, 3]
对于一些简单的函数,可以使用lambda匿名表达式来取代函数的定义,这样可以简化代码:
>>> add =lambda x,y:x+y
>>> add(1,2)
3
>>> list(map(lambda x:x+1,[1,2,3,4,5,6,7]))
[2, 3, 4, 5, 6, 7, 8]
这样可以利用一个简化的表达式来简化代码,提高代码的可读性
迭代器与生成器:
迭代器不需要事先准备好整个迭代过程的所有元素,可以使用next()来访问元素,python中的容器,例如list,dic,set等都属于可迭代对象,对于这些容器,可以使用iter()来封装成迭代器:
>>> x=[1,2,3]
>>> y=iter(x)
>>> z=iter(x)
>>> next(y),next(y),next(z)
(1, 2, 1)
迭代器之间相互独立。
还可以使用生成器表达式,与list相似,不过需要使用()括号。
>>> a=(x for x in range(10))
>>> next(a)
0
>>> next(a)
1
>>> next(a)
2
最为常见的是使用yield关键字来创建一个生成器,例如下面代码,调用f()函数后会返回1并保持住,第二次调用会返回2并保持住。
>>> def f():
... yield 1
... yield 2
... yield 3
...
>>> f1 = f()
>>> print(list(next(f1) for i in range(2)))
[1, 2]
这些便是实际开发中常用的一些重要特性,尤其在机器学习和深度学习中运用很频繁。如果有错误的话可以指出来,或者有更好的方法也可以提出来。