python初探

  

  python近两年似乎已经很热了,不了解一下怎么能行呢,似乎python最大的优点就是简洁、易懂、优雅。目前豆瓣、知乎等后台服务使用的也都是python语言。 python一般可以用于网站服务、小工具、数据分析等工作。它作为高级语言,和js一样,是解释型语言,所以运行速度上会比较慢,但是在网络服务上,相对于网速的缓慢,运行速度也就不是很大的问题了。 

  

python环境安装

  在windows环境下,我们可以进入这里下载安装,安装的过程中需要勾选添加到环境变量,这样,在命令行操作过程中就不会出现问题了。 安装完成后,打开命令行,输入python,就进入交互模式了,输入exit(),即可退出python环境。 另外,这个安装的python环境就是最为通用的python解释器cpython,之所以称为cpython,是因为这个解释器是通过c语言编写的。

  

 

 

简单的例子

  我们在运行中,输入cmd进入的是命令行模式,而如果输入python,就可以进入python交互模式,因此如果我们希望运行python代码,就需要进入python交互模式,上面的图片也就是python交互模式。

  我们还可以在进入python交互模式后,输入 print('hello world!') ,即这是python的输出字符串的语法,当然,我们也可以使用print输出多个字符串,需要使用逗号隔开,如 print('hello', 'world', 'wayne'),并且其中也是可以接受数字的,如print(200)。 

  另外,我们可以编辑一个.py后缀的python文件,然后在命令行下运行,即 python foo.py就可以执行这个foo.py文件了。

 

  如上,我在sublime编辑器中新建一个foo.py,其中python语句是没有分号和花括号的,因为这样更加接近于自然语言。 然后在命令行中运行:

  这样,一个简单的python程序就出来了。 

 

 

输入输出

  python中的输入输出也是非常简单的。 

  输出就是使用print,可以接受多个参数,参数的类型可以是数字也可是字符串,当然字符串中可以有\t和\n等。

  输入当然也是交互中必不可少的,为name(),这个在执行之后,会提示用户输入,name()函数可以接受一个字符串作为参数,如name('请输入你的名字:'),举例如下:

name = input('请输入你的名字:')
print('你好,', name)

 

 

  

python基础知识

   python作为计算机语言,那么语言就必须要计算机理解,必须有一定的规则,而不是像自然语言很随意。python的语法是比较简单的,采用的时特有的缩进的方式,并且没有像js那样分号作为语句的结束,且语句在使用 : (冒号)结尾时,缩进的语句视为代码块,缩进没有特定的要求,但是一般以4个空格为准,且python是大小写敏感的。 python中使用#作为注释的标志。且python中不需要声明变量,直接使用就可以了。如下所示:

num = input('请输入一个数字')
if int(num) > 0:print('您输入的数字大于0')
elif int(num) < 0:print('您输入的数字小于0')
else:print('您输入的数字等于0')

我们可以看到python中的else if需要写成elif,否则会报错,这样也正是python简洁的体现,而通过input()函数输入的是一个字符串,在直接判断与0的大小时会出错,所以我们可以使用int()来讲字符串强制转化为数字,就可以进一步的比较了。 如下:

 

python中的数据类型和其他语言都是大同小异的,包括整数、浮点数、字符串、布尔值以及运算。其中布尔值有些许不同,如True表示真,而False表示假,第一个字母需要大写,另外,我们可以使用 and、 or、 not 来表示布尔值的与、或、非运算,这使得python更加接近自然语言。python中使用None表示空值。 它的变量同样是大小写英文、数字和_组合,且不能使用数字作为开头 。 python是动态语言,因为他的变量类型和js一样是可以变动的,而不像java。 另外,python中的除法有两种,一种是普通的 / ,这种方法不管除数和被除数为整数或者浮点数,得到的结果都是浮点数;而 // 这种除法得到的结果总是整数。同样,python使用 % 作为求余的操作符。 

 

 

  

python字符串和编码

  对于字符串而言,最为复杂的就是编码问题了,因为计算机只能处理0/1数字,所以要处理文本,就必须先把文本转换为数字才能处理。

  1个字节是8个bit,所以一个字节能表示的最大的整数是255,也就是说用一个字节编码的方式可以最多表示255种字符。而最早是美国人发明了计算机,他们使用的语言只有a-zA-Z和其他字符一共127个,即ASCII编码。但对于十万个汉字的编码,显然ASCII编码是做不到,而两个字节可以表示6万多种字符,所以,中文的编码至少需要2个字节,于是中国制定了GB2312编码。 但世界上有上百种文字,日本要把日文编到Shift_jis中,韩国把韩文编到Euc-kr中,显然,这样就没有了统一的标准,而使得有多种文字编码的文本中,很可能出现乱码的情况。

  因此,Unicode编码应运而生,即union code,统一的编码,这种编码包含了各国文字,使得文本可以通用,而不必担心出现乱码的情况,Unicode编码一般是两个字节,但两个字节只能表示6万多字符,所以有时也会有四个字节的情况,但对于常用的文字,一般都是两个字节。但如果一直使用Unicode编码,问题是如果通篇英文,Unicode编码会比ASCII编码多出一倍的存储空间,这是非常浪费的,于是,又出现了 UTF-8编码,这种编码把一个unicode字符根据不同的数字大小编为1 - 6个字节,常用的英文字母还是1个字节(可以将ASCII编码看做UTF-8编码的一部分),而汉字通常是3个字节,所以如果传输中包含了大量的英文字符,使用UTF-8编码会更加节省空间。  

  实际上,在计算机内存中,统一使用Unicode编码,当需要保存到硬盘或者传输的时候,就转换成UTF-8编码,如浏览网页的时候,服务器会把动态生成的Unicode内容转换成UTF-8再传输到浏览器。这样我们也就理解HTML文件中 <meta charset="UTF-8"/>的含义了。

  而python中字符串也是Unicode编码的,即它支持多种语言,而不会出现乱码。对于单个字符的编码,python提供了ord()函数获取字符的整数表示(Unicode编码的整数表示),chr()函数把编码转换为对应的字符。 

  而如果我们知道字符的整数编码,还可以用十六进制来写str,如

>>> '\u4e2d\u6587'
'中文'

  其中,\u这个转义字符表示Unicode编码,而4e2d是十六进制的编码,即中这个汉子用了两个字节来表示,同样文也用了两个字节来表示。

  另外,Python中提供了encode()函数和decode()函数来进行编码和解码,len()函数来返回字符长度。

 

   在python中,也经常需要格式化,和c语言是非常类似的,%s为字符串,%d为整数,%f为浮点数,%x为十六进制数,并且如果在表示中有%我们需要进行转义%%,另外,如果不知道该用哪个占位符,统一使用%s就可以了。format()函数也是同样的表达方式,但相对于前者还是稍显复杂,如下所示:

 

 

使用list和tuple

  在c++中有List和tuple这两种数据类型,而在python中同样也有。 list比tuple有更多的操作方法,但是tupple一旦定义不能被改变,因而更加安全,下面做简单介绍。 

  

  如上所示,我们可以直接定义rel = ['wayne', 'hedy', 'baby']这个list,使用len()函数可以获取这个list的长度,使用rel[0]这种下标可以获取其中的元素,通过append()可以将元素添加到末尾,通过pop()可以弹出(删除)最后一个元素,我们还可以通过insert()将元素插入到指定位置,使用pop(num)将指定位置的元素删除,并且使用 rel[num] = str的方式直接替换list中的某个元素。 因此,list数据类型还是非常方便,操作起来更为简单、强大。 

 

 

  tupple和list非常类似,只是在定义的时候使用的时()而非[],并且最为本质的区别在于tupple一旦定义只能访问不能修改,当然也就没有append、insert、pop这些方法了,优点就是更安全,如果可以使用tupple就尽量不要使用list。 

  

  可以看到,我们定义了rel之后,只能进行访问,而在试图修改时发生了错误。 

 

  

循环

  python中有两种循环。一种是使用for...in循环,另外一种便是while循环。 

rel = ['wayne', 'hedy', 'baby']
for name in rel:print(name, '\n')

  以上是使用for...in循环rel这个list,注意,以为for下一行缩进,所以for语句后有 : (冒号)。

  另外,python中提供了range函数,如list(range(50)) 可以生成一个0到49的list,而不需要我们手写了。同样,for...in也可以使用range,如下:

max = int(input('请输入一个正整数:'))
sum = 0
for a in range(max + 1):sum = sum + a
print('1到%d' % max, '的和为:',sum )

  运行结果如下所示:

  

 

  而while循环和其他语言的循环也是类似的,如下所示:

import time
n = int(input('请输入一个正整数:'))
while n > 0:print(n, 's\n')time.sleep(1)n = n - 1
print('over!')

 这里我们在python文件中直接引入了time模块,通过time模块的sleep函数使得程序在运行到这一语句时暂停1s中而继续运行。 

  同样的,在python中也可以正常使用break和continue关键词

 

  

 

dict和set

  在c++中提供了map这样的数据结构,这种数据结构是含有多个k-v对,具有一一映射的特点,而在python中也提供了同样的数据结构,只是名称为dict,即dictionary字典,使用起来非常简单。且list使用的时[],tuple使用的时(),这里的dict使用的是{},如下:

  

  如上所示,我们使用{}来定义这个dict,并且可以使用d[key]的方式获取到它的值,另外dict提供了get方法来查找相应的value,当然,如果不存在,则什么也不会返回。 且可以通过d[key]的方式来添加映射。 通过pop()函数来删除其中的某个k-v对。注意:dict中的key是不能重复的。

 

  那么set是怎么样定义的呢?set和dict非常类似,但是set中只有key而没有value,相同点在于key都是不能重复的,如果重复,则也会自动过滤, set有add方法添加key, 通过remove方法删除, set可以看成数学意义上的无序和无重复元素的集合,因此,两个set可以做数学意义上的交集、并集等操作。 如下所示:

  

  如上所示:我们可以看到,set()函数中实际上就是一个list,通过add添加key,通过remove删除key,通过s1 = s来赋值,且两个set之间可以进行并集和交集的运算。

       

  另外,我们可以看到,这里s3本来是有重复数字的,但在建立后生成时,重复数字会自动被删除,这便是set的一大特点。

  

  

python函数

  python中内置了很多函数,比如之前的chr()等,这些函数是可以直接调用的,文档中查看更多内置函数。https://docs.python.org/3/library/functions.html#abs

  比如abs即求绝对值的函数、max函数返回多个参数中最大的一个、min函数等等。 更多示范可以看另外一篇文章。

  

  

python切片

  即和js中的slice方法是类似的,但是python中是没有slice的,而是使用了更为简单的方式,如下:

>>> L = [0,1,2,3,4,5,6,7,8,9,10]
>>> L[0:3]
[0, 1, 2]
>>> L[:3]
[0, 1, 2]
>>> L[2:4]
[2, 3]
>>> L[-2:]
[9, 10]
>>> L = list(range(20))
>>> L
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
>>> L[0::2]
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
>>> L[::2]
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
>>> "wayne-zhu"[0:5]
'wayne'
>>> t = (1, 56, 74, 85, 9)
>>> t[0:3]
(1, 56, 74)

  即对于list而言,指定开始和结束的后一个位置,即可进行切片;

       如果是从0开始,则数字0是可以省略的;

       如果是负数,就是从倒数第几个开始,最后一个是-1;

       我们还可以指定每隔几个数取出一个数出来;

       字符串也可以看成一种list,也是可以用类似的方法的;

  tupple和list一样,只是tupple不可变,但也是可以使用这样的方法。 

 

  下面是使用python定义的trim函数,实现方式如下:

def trim(str):old = stri = 0while True:if str[i] == ' ':str = str[1:]else:breakj = len(str) - 1while True:if str[j] == ' ':str = str[0:j]j = len(str) - 1else:breakprint(old,'trim之后为:',str,'trim之后的字符长度为', len(str))trim('   wayne       ')

   我们首先将str保存起来,然后对字符串的前半部分进行处理 - 如果第一个字符是空格就slice; 对于字符串的后半部分做出同样的处理。 结果如下:

C:\Users\Administrator\Desktop>python foo.pywayne        trim之后为: wayne trim之后的字符长度为 5

 

 

python迭代

  在js、c、c++等编程语言中,for循环都是大同小异的,如for(int i = 0; i < 10; i++),而在python中,for循环变得更加抽象了,如下所示:

d = {'wayne': 22, 'hedy': 18, 'baby': 0}
print('key:')
for key in d:print(key)print('\nvalue')for value in d.values():print(value)print('\nkey and value')
for k,v in d.items():print(k, v)

  结果如下:

key:
wayne
hedy
babyvalue
22
18
0key and value
wayne 22
hedy 18
baby 0

  

  另外,字符串也是可迭代对象。 

print('\n 字符串的迭代:')
s = 'wayne-zhu'
for ch in s:print(ch)

  这样,可以依次将字符打印出来。

  

  那么我们如何判断一个对象是否为可迭代对象呢?可以通过collections模块的Iterable类型判断:

from collections import Iterable
print(isinstance('wayne', Iterable)) #True
print(isinstance([1, 2, 3], Iterable)) #True
print(isinstance(888, Iterable)) #False

  如上所示,我们通过isinstance就可以判断了。这里其实是判断'wayne'、[1, 2, 3]、888是否是Iterable这个类的实例。 该函数返回一个布尔值,True或者False。

 

  另外,list是没有下标的,我们怎么实现呢?可以使用enumerate()函数,如下所示:

L = [666, 888, 520, 521, 1314]
for k,v in enumerate(L):print(k, v)

  最终结果如下所示:

0 666
1 888
2 520
3 521
4 1314

  可以看到,这样我们就可以获得下标了。 

 

练习: 使用迭代的方法获得一个list中的最大值和最小值,并返回这个list的tupple:

L = [666, 888, 520, 521, 1314]
max = L[0]
min = L[0]
for k,v in enumerate(L):if L[k] > max:max = L[k]if L[k] < min:min = L[k]print(max, min)
print(tuple(L))

结果如下;

1314 520
(666, 888, 520, 521, 1314)

 

 

 

python列表生成式

  生成1到10的列表,使用下面的语法:

>>> list(range(1, 11))
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

  

  如果要生成[1 * 1, ... 10 * 10]的列表呢?

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

  上面这种语法是常见的,即直接将迭代式子写在[]中,前半部分是我们所需要的表达式 。

 

  另外,对于字符串而言,我们通常需要生成某个字符的全排列,可以这样:  

>>> [m + n for m in 'xyz' for n in 'xyz']
['xx', 'xy', 'xz', 'yx', 'yy', 'yz', 'zx', 'zy', 'zz']

  上面的式子就生成了xyz的全排列,xx、xy、xz、yx、yy、yz、zx、zy、zz, 当然我们还可以生成不同字符串之间的全排列,这是非常有用的。

 

  下面的代码还可以将list中的所有字母编程小写:

>>> L = ['WAYNE', 'HEDY', 'BABY']
>>> [s.lower() for s in L]
['wayne', 'hedy', 'baby']

  其中 lower() 函数是将字符串转化为小写, 而upper()函数可以将字符串转化为大写,如下所示:

>>> wayne = ['wayne', 'hedy', 'baby']
>>> [s.upper() for s in wayne]
['WAYNE', 'HEDY', 'BABY']

  注意:上面的s.upper()也可以写成 str.upper(s), lower同理。

 

  另外,如果我们的L为['Wayne', 'Hedy', 18, None]如果直接使用列表生成式会因为18不是字符串而出错:

>>> L = ['Wayne', 'Hedy', 18, None]
>>> [str.lower(i) for i in L]
Traceback (most recent call last):File "<stdin>", line 1, in <module>File "<stdin>", line 1, in <listcomp>
TypeError: descriptor 'lower' requires a 'str' object but received a 'int'

  我们试着使用if语句以及isinstance来修改:

>>> [str.lower(i) for i in L if isinstance(i, str)]
['wayne', 'hedy']

  即在这个列表生成式中直接加入if语句,然后使用isinstance判断i是否是str(字符串)类型即可。

 

 

python生成器

     创建一个列表就会打印出所有的元素,而如果这个列表的元素很多而我们仅仅需要访问其中的前几个元素,那么生成整个列表就会造成浪费。 而如果列表元素可以按照某种方法算法推算出来,那么我们是否可以在循环的过程中不断地推算出后续的元素呢?这样就不必创建完整的list,而节省了大量的空间。 在python中一边循环一边计算后续元素的机制,就称为生成器

  比如,我们创建了一个1到100的list,如下:

>>> list(range(1, 100))
[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, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99]

  但如果我们只需要1到100的前几个元素,那么后续元素占用的内存就造成了浪费。 而生成器(generator)是下面这样的:

>>> g = (x for x in range(1, 100))
>>> g
<generator object <genexpr> at 0x02C41E70>

  如果我们希望访问其元素,可以使用next()进行访问,next函数接受一个参数,即这个生成器g,如下所示:

>>> next(g)
1
>>> next(g)
2
>>> next(g)
3
>>> next(g)
4
>>> next(g)
5

  如上所示,每调用一次next()函数,就会返回生成器的下一个元素,生成之后,才会给这个元素分配内存,这样便大大节省了内存空间,即做到按需分配。

  又比如下面的生成器:

>>> g = (x for x in range(1, 5))
>>> g
<generator object <genexpr> at 0x02C6F0F0>
>>> next(g)
1
>>> next(g)
2
>>> next(g)
3
>>> next(g)
4
>>> next(g)
Traceback (most recent call last):File "<stdin>", line 1, in <module>
StopIteration

  可以看到,这里我们定义了元素从1到4的生成器,打印g,显示g为一个generator,然后调用next()函数,返回generator的元素,每调用一次,返回下一个元素,但是当最后一个元素4已经打印出来后,再调用next(),就会报错:即stopIteration,停止遍历。

  当然,更多情况下,我们还可以使用for来遍历生成器,因为生成器是可遍历的,如下:

g = (x * x for x in range(1, 5))
for n in g:print(n)

  打印出的结果如下所示:

C:\Users\Administrator\Desktop>python foo.py
1
4
9
16

  

  上面,我们都是直接使用()加表达式生成了一个生成器,但如果推算较为复杂的情况下,我们还可以使用函数的方式。 首先,我们可以看到如果斐波那契数列的函数生成:

def fib(max):n, a, b = 0, 0, 1while (n < max):print(b)a, b = b, a + bn = n + 1return 'done'fib(5) # 1 1 2 3 5
  • 这里需要注意:python中定义变量更为简洁,即这里的n, a, b = 0, 0, 1简洁的一句代码便定义了三个变量
  • a, b = b, a + b的定义也是非常简洁,这里没有使用中间变量保存a,而是使用了tupple的数据结构,即t = (b, a + b)、 a = t[0]、 b = t[1]。

 

  而如果我们这里将 print(b) 修改为 yield(b), 就是一个生成器了。 对于生成器来说,每次yield的时候,都会暂停,必须使用next()才能继续执行下面的程序。所以就成了生成器了。而对于生成器来说,我们使用for..in来调用会更加方便一些:

def fib(max):n, a, b = 0, 0, 1while (n < max):yield(b)a, b = b, a + bn = n + 1return 'done'g = fib(5) # 1 1 2 3 5for x in g:print(x)

  如上所示:我们可以看到这时的fib就是一个生成器了,且可以使用for...in遍历。

  

  

 

python迭代器

  之前我们提到过:"abc"、[]等都是Iterable(可迭代的),但是他们并不是迭代器,如下所示:

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

  我们可以看到:[]、{}、'abc'、(x for x in range(1, 10))都是可迭代的,只有628这个数字肯定不是的。如果换做迭代器呢?

from collections import Iterator # 注意:这里import要换做Iterator
print(isinstance({}, Iterator)) #False
print(isinstance('abc', Iterator)) #False
print(isinstance((x for x in range(1, 10)), Iterator)) #True
print(isinstance(628, Iterator)) #False

  这里,我们可以看到,只有(x for x in range(1, 10))是迭代器,其他都不是迭代器,这是为什么呢?

>>> g = (x for x in range(1, 10))
>>> next(g)
1
>>> next(g)
2
>>> next(g)
3
>>> next(g)
4

  我们可以看到,(x for x in range(1, 10))这个表达式可以调用next函数,知道最后元素迭代完成,会报错:stopIteration。 

  而'abc'呢?如下所示:

>>> g = 'abc'
>>> g
'abc'
>>> next(g)
Traceback (most recent call last):File "<stdin>", line 1, in <module>
TypeError: 'str' object is not an iterator

  如上所示:使用'abc'的next函数时,会直接报错:'str' object is not an iterator。即字符串不是一个迭代器。 对于list、disc也同样如此,所以这里只有tupple才是Iterator。

>>> g = {'wayne':22, 'hedy': 18}
>>> g
{'wayne': 22, 'hedy': 18}
>>> next(g)
Traceback (most recent call last):File "<stdin>", line 1, in <module>
TypeError: 'dict' object is not an iterator

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

   但是,我们是可以将非Iterator强制转换为Iterator的。使用iter()函数即可。但前提是它们必须是Iterable,对于628这样的not Iterable的类型,使用iter()也是无济于事的。 如下所示:

>>> s = 'abc'
>>> s
'abc'
>>> i = iter(s)
>>> i
<str_iterator object at 0x039F4670>
>>> next(i)
'a'
>>> next(i)
'b'
>>> next(i)
'c'

   628不是Iterable,如下:

>>> n = 628
>>> i = iter(n)
Traceback (most recent call last):File "<stdin>", line 1, in <module>
TypeError: 'int' object is not iterable

  可以看到,对于int类型的628,如果使用iter,也是会报错的 --- 'int' object is not iterable

 

  那为什么list不是Iterator呢? 这是因为Iterator是惰性的,并没有一次性获取到所有的数据,是无限制的,而list是一开始就存储了数据。

 

  

 

  

高阶函数

  即函数中接受函数作为参数的函数就是高阶函数。 如map、filter等。又如sorted函数,如下:

>>> sorted([35, -56, 85, 69])
[-56, 35, 69, 85]
# 如上,这里没有接受函数作为参数,只是按照从小到大的排序
>>> sorted([35, -56, 85, 69], key=abs) [35, -56, 69, 85] # 这里我们接受了abs函数作为第二个参数,即按照其绝对值大小进行排序
>>> sorted(['bob', 'about', 'Zoo', 'Credit']) ['Credit', 'Zoo', 'about', 'bob'] #这里我们直接对字符串进行排序
>>> sorted(['bob', 'about', 'Zoo', 'Credit'], key=str.lower) ['about', 'bob', 'Credit', 'Zoo'] #这里我们将所有的字符串按照转化为小写字母之后进行排序
>>> sorted(['bob', 'about', 'Zoo', 'Credit'], key=str.lower, reverse=True) ['Zoo', 'Credit', 'bob', 'about']
#如果我们希望反向,只需要再添加一个参数 -- reverse = True

 

 在python中,也是存在匿名函数的概念的,如下:

>>> f = lambda x: x * x
>>> f(5)
25

  如上所示: 等式右边的就是一个匿名函数,而 lambda 关键词就表示在声明一个匿名函数,冒号之前的x表示接受的参数,之后表示返回的值。

 

  

 python装饰器

  如下所示:定义一个now函数,因为函数是一个对象,所以可以赋值给f(另外一个变量),然后我们再调用这个函数即可。

  

>>> def now():
...     print('20180324')
...
>>> f = now
>>> f()
20180324

  在python中,我们可以调用now或者f的__name__属性,以获取到该函数的函数名:如下:

>>> now.__name__
'now'
>>> f.__name__
'now'

  

  那什么是装饰器呢?比如,我们希望函数调用前后自动打印日志, 但是又不希望修改now()函数的定义,这种在代码运行期间动态增加功能的方式,称之为装饰器。并需要用到@的Python语法

  下面我就定义一个装饰器函数用于打印日志:

def log(func):def wrapper(*args, **kw):print('call %s():' % func.__name__)return func(*args, **kw)return wrapper

  这个装饰器函数就是一个高阶函数(接受一个函数作为参数),然后定义了一个wrapper函数返回fun()执行后的结果。然后我们将Log置于函数的定义处:

def log(func):def wrapper(*args, **kw):print('call %s():' % func.__name__)return func(*args, **kw)return wrapper@log
def now():print('20180324')now()

  我们执行之后,得到如下结果:

call now():
20180324

  至于上面的格式,即将@log放在了now函数的定义之前,相当于在定义之后执行了函数 now = log(now),即把这个now函数从新进行了包装、修饰,即为装饰器函数。因此,我们写成下面的方式也是可以的:

def log(func):def wrapper():print('call %s():' % func.__name__)return func()return wrapper@log
def now():print('20180324')now()

  之前添加了的一些参数是为了更多的使用扩展性。即now()函数执行之后,即调用了这个装饰器函数@log,返回了wrapper函数,最后我们调用now(),就执行了这个wrapper函数,并返回结果。

  这里,我们可以将@log解释为如下的执行方式:

def log(func):def wrapper():print('call %s' % func.__name__)return func()return wrapperdef now():print('20180324')now = log(now) # 这样,会fanhuiwrapper函数,然后调用now,就是执行了wrapper函数,首先打印了内容,再执行真正的now函数。now()

  

  上面是最简单的方式,而如果这个装饰器自身需要接受参数,那么如果使用上面的这种方式就无法满足了,我们必须再包裹一层函数,如下所示:

def log(text):def decorator(func):def wrapper():print('call %s %s' % (text, func.__name__))return func()return wrapperreturn decorator  @log('extro')
def now():print('20180324')now()

  思路非常明确,即包含了一层decorator函数,我们将text传给装饰器,实际上,@log('extro')的另外一种真是的写法如下:

def log(text):def decorator(func):def wrapper():print('call %s %s' % (text, func.__name__))return func()return wrapperreturn decorator  def now():print('20180324')now = log('extor')(now)now()

  最终的结果如下:

call extor now
20180324

  

   但这里还是有问题的,当我们在程序的最后加上 print(now.__name__)的时候,我们可以发现最终输出的时 wrapper 而不是 now,这不是我们希望看到的,而我们修改之后最终的结果如下:

import functoolsdef log(text):def decorator(func):@functools.wraps(func)def wrapper():print('call %s %s' % (text, func.__name__))return func()return wrapperreturn decorator  def now():print('20180324')now = log('extor')(now)now()print(now.__name__)

 

 

 

python偏函数

  偏函数还是很好理解的,我们从一个简单的例子说起。

  比如我们可以使用int('1000')将字符串'1000'转化为数字,这个转化过程默认是十进制的。 而如果我们希望转化为2进制的,我们可以这样调用:int('1000', base=2)或者是int('1000', 2)。 但是如果我们需要大量的转化为二进制数字的调用,每次都int('1000', 2)难免比较麻烦,所以我们可以封装为一个函数再调用,如下:

def int2(str):return int(str, base=2)print(int2('100')) #4
print(int2('10')) #2
print(int2('1')) #1

  如上所示,这样,我们就可以很轻松的进行转化了。但python中的functools模块本身有这个作用,如下:

import functools
int2 = functools.partial(int, base=2)print(int2('100')) #4
print(int2('10')) #2
print(int2('1')) #1

  即这里我们使用了functools.partial函数,这就是偏函数,即把一个函数的某些参数给固定住(也就是设置为默认值),返回一个新的函数,调用这个函数会更加简答。而我们也可以继续个性化,如下:

import functools
int2 = functools.partial(int, base=2)print(int2('100',base=10)) #100
print(int2('10')) #2
print(int2('1')) #1

  如上,可以看出来,这个和我们上面自己定义的int2是相同的,总之,偏函数可以固定住某些参数,使我们的操作更加简单。

 

 

 

 

python模块

  和JavaScript是类似的,大多数编程语言都是要进行模块化开发的,因为当工程越来越复杂的时候,不可能始终使用一个文件,而是根据程序块的不同作用分配到模块中,这样,有利于开发的高效性,另外,同一个模块还可以在多处使用,这样有利于提高程序的重用性,python也不例外。   

  python中,一个.py文件就可以看做一个python模块,模块很好地解决了变量命名冲突的问题,并且通过模块,但当模块多时,也不可避免出现重名,在python中也就有了包的概念,如一个mymodule包,下面有util.py模块、anto.py模块,并且一定还有一个__init__.py模块,如果没有,Python将不认为mymodule是一个包,通过这个包,我们的模块名称更具辨识性,如util.py模块又称为mymodule.util模块等等。

  使用模块我们很清楚,直接import 即可,但是如果自定义一个自己的模块呢?如下所示(该文件名为hello.py):

'my first module'__author__ = 'Wayne Zhu'import sysdef test():args = sys.argvif len(args) == 1:print('hello world!')elif len(args) == 2:print('hello %s' % args[1])else:print('Too many arguments!')if __name__ == '__main__':test()

  Ok! 这就是一个标准的模块了,其中'my first module'是这个py文件的第一个字符串,默认为该文件的说明性注释; 而__author__ = 'Wayne Zhu'中的__author__是专有变量,是声明这个文件的作者,接下来我们引入了sys模块,通过这个模块我们可以获得用户在命令行输入的参数;最后我们判断__name__是否为__main__,如果是执行当前文件,则是,作为入口文件,当然是main,如果是被其他Python文件调用,则不是。

  其中,sys的argv参数是命令行的参数,如python hello,则argv是一个list,且数量至少为1,这里就是['hello'],而如果用户执行时是 python hello wayne-zhu,则sys.argv的元素个数为2,即['hello', 'wayne-zhu']。

  该模块如果被直接调用,最终的结果如下:

C:\Users\Administrator\Desktop>python hello.py
hello world!C:\Users\Administrator\Desktop>python hello.py wayne-zhu
hello wayne-zhuC:\Users\Administrator\Desktop>python hello.py wayne hedy
Too many arguments!

  而如果我们是在python交互环境中引入hello模块,则不会有输出,如下:

>>> import hello
>>> hello.test()
hello world!

  但是我们可以直接通过hello调用test函数。 

 

 

python变量

  这里主要说的还是变量的可访问性。

  • 公开变量,abc/foo等,这些变量是公开的,允许被模块内外访问的。
  • 模块内变量/私有变量,_abc、_foo等,即简单的通过_来确定,但实际上语法并没有限制,只是方便程序员观察使用。
  • 特殊变量,__name__、__author__等,这些变量和公开变量并没有什么区别,只是他们有特殊的用途,如__author__表示当前模块的作者名; __name__和'__main__'判断是否相等来判断是直接执行还是间接调用。
def _private_1(name):return 'Hello, %s' % namedef _private_2(name):return 'Hi, %s' % namedef greeting(name):if len(name) > 3:return _private_1(name)else:return _private_2(name)

  比如上面的例子中,_private_1和_private2是模块内使用的,而greeting是暴露出去的,可以被其他的模块使用。

 

  hello.py如下:

'my first module'__author__ = 'Wayne Zhu'import sysdef test():args = sys.argvif len(args) == 1:print('hello world!')elif len(args) == 2:print('hello %s' % args[1])else:print('Too many arguments!')def speak(str='great!'):print(str)if __name__ == '__main__':test()
View Code

  foo.py如下:

import hello
hello.test()
hello.speak('I am handsome')

  当我们执行foo.py时,最终的结果如下:

hello world!
I am handsome

  可见,我们成功的在foo.py中引用了hello模块,并使用了hello模块暴露的speak函数和test函数,但是根据hello模块test编写形式而言,test主要是为了作模块内测试的,并非暴露给其他模块使用。但语法上并没有任何问题。

 

 

安装第三方模块

    我们在命令行中输入pip,如果正常,则说明可以通过pip安装第三方模块,这个和前端中的npm是类似的,并且,一般的开发者如果要提供自己的模块,需要到pypi.python.org中注册,和前端需要到npm.org中注册时一样的,比如我们要安装该库的名称是 Pillow,那么安装Pillow的命令就是:

pip install Pillow

  如下所示:

C:\Users\Administrator\Desktop>pip install Pillow
Collecting PillowDownloading Pillow-5.0.0-cp36-cp36m-win32.whl (1.4MB)100% |████████████████████████████████| 1.4MB 655kB/s
Installing collected packages: Pillow
Successfully installed Pillow-5.0.0
You are using pip version 9.0.1, however version 9.0.3 is available.
You should consider upgrading via the 'python -m pip install --upgrade pip' command.

  但是,这里下载的位置在C:\Users\Administrator\AppData\Local\Programs\Python\Python36-32\Lib\site-packages,所以,如果我们希望自己新建一个py文件可以import Pillow,就必须要将Pillow这个文件夹放在和当前py文件的同级目录下,否则就引入不了。

  

  同样的,Flask这个web框架就是python框架,安装如下:

pip install flask

  然后,我们再新建下面这个文件:

from flask import Flaskapp = Flask(__name__)@app.route('/')
def hello_world():return 'Hello Flask!'if __name__ == '__main__':app.run()

  然后在命令行中执行这个文件:

C:\Users\Administrator\Desktop>python foo.py* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)

  于是我们就可以看到浏览器如下:

  即通过这个Flask库,我们就自己快速搭建了一个服务器。但是我们可以发现一个问题,就是每次在我们修改文件内容时,比如返回 Hello Wayne!,然后我们去刷新浏览器,但显示内容并没有刷新,这是因为我们没有开启DEBUG模式,如果要开启,我们需要在环境变量中添加 FLASK_DEBUG, 值为1,如下:

  这样,我们在运行该文件时,内容时这样的:

C:\Users\Administrator\Desktop>python foo.py* Restarting with stat* Debugger is active!* Debugger PIN: 109-984-470* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)

  即调试模式开启,我们就可以进行调试了。且每次我们修改文件并保存后,服务器就会重启,然后我们刷新浏览器,内容就会发生改变了!这方面的使用还是比node更加方便一些。

  

  另外还有很多有用的模块,如果我们都这样下载,未免太慢,所以推荐使用Anaconda,这个平台一旦下载就意味着下载了数十个库文件,这样是非常有用的,我们可以在这里下载。  

  安装完成之后,输入python,我们就可以看到Anaconda的相关信息了。然后,对于一些常见的模块我们就不需要再自己下载,而是直接使用即可。但有些情况下,安装之后输入python还是没用,这时我们可以将anaconda的安装路径加入到path中即可,如:

  

 

 

  注意:现在,不下载也是可以的,最多全部下载一遍,不适用Anaconada也是一样的,不必过分纠结。


 

  另外,之前我们说过:每次install一个模块之后,可以通过everything这个工具找到该文件,然后将当前python文件和库文件放在同级目录下才可以使用,但这样比较麻烦,我们可以将存放install的库的路径加入到sys模块的path变量中,这样我们就不用每次都把下载到的文件再手动放在一起了。

 import sys
>>> sys.path
['', 'C:\\Users\\Administrator\\AppData\\Local\\Programs\\Python\\Python36-32\\python36.zip', 'C:\\Users\\Administrator\\AppData\\Local\\Programs\\Python\\Python36-32\\DLLs', 'C:\\Users\\Administrator\\AppData\\Local\\Programs\\Python\\Python36-32\\lib', 'C:\\Users\\Administrator\\AppData\\Local\\Programs\\Python\\Python36-32', 'C:\\Users\\Administrator\\AppData\\Local\\Programs\\Python\\Python36-32\\lib\\site-packages']
>>> sys.path.append('C:\\Users\\Administrator\\AppData\\Local\\Programs\\Python\\Python36-32\\Lib\\site-packages')

  我们可以看到: 这里的sys.path是一个list,所以可以使用append方法来添加元素。

  

 

 

 

  

 

  

 

 

 

 

 

  

  

  

 

 

 

 

 

 

 

 

 

 

参考文章:廖雪峰博客、python文档

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/283842.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

solr5.5索引mysql数据(新手总结)

一 solr5.5环境部署到Eclipse(luna版&#xff09; solr部署参见&#xff1a;http://blog.csdn.net/csmnjk/article/details/64121765 二 Ik分词器设置 IK分词器设置参见:http://blog.csdn.net/csmnjk/article/details/51693578 solr4版本的schema.xml文件对应solr5版本的manage…

老板加薪!看我做的WPF Loading!!!

老板加薪&#xff01;看我做的WPF Loading&#xff01;&#xff01;&#xff01;控件名&#xff1a;RingLoading作者&#xff1a;WPFDevelopersOrg原文链接&#xff1a; https://github.com/WPFDevelopersOrg/WPFDevelopers.Minimal框架使用大于等于.NET40&#xff1b;Visua…

如何避免下重复订单

电子交易的一个很基本的问题&#xff0c;就是避免用户下重复订单。用户明明想买一次&#xff0c;结果一看下了两个单。如果没有及时发现&#xff0c;就会带来额外的物流成本和扯皮。对商家的信誉也不好看。 从技术上看&#xff0c;这是一个分布式一致性问题&#xff1b;但实际…

图像分类学习笔记

1.计算机认识图像的方式&#xff1a;都是数字。例如一个 128X128 的3通道的图片 是由 128X128X3个数字 组成的。 2.面临的难点&#xff1a;一幅图可以说明。 3.分类器 A&#xff1a;Nearest Neighbor Classifier&#xff1a;与CNN无关&#xff0c;但是可以帮助我们理解一下分类…

知物由学 | 干货!一文了解安卓APP逆向分析与保护机制

“知物由学”是网易云易盾打造的一个品牌栏目&#xff0c;词语出自汉王充《论衡实知》。人&#xff0c;能力有高下之分&#xff0c;学习才知道事物的道理&#xff0c;而后才有智慧&#xff0c;不去求问就不会知道。“知物由学”希望通过一篇篇技术干货、趋势解读、人物思考和沉…

[转]以终为始,详细分析高考志愿该怎么填

为什么写这篇文章&#xff1f; 之所以写本文&#xff0c;是因为我自己有用处。 我简要介绍&#xff0c;长话短说。我从一个普通的211本科毕业&#xff0c;已经接受社会"毒打"多年&#xff0c;回想起高考填志愿&#xff0c;依然会觉得有些许遗憾。我在贵州省的一个小县…

ASP.NET Core 中的重定向

前言在《如何使用ASP.NET Core Web API实现短链接服务》中&#xff0c;我们使用了Redirect方法返回跳转状态码:[HttpGet("{shortUrl}")] public IActionResult GetUrl(string shortUrl) {var hashids new Hashids("公众号My IO", minHashLength: 6);var i…

客户端应用试用限制设计

1.概要最近接到公司安排的任务给客户端设计一个“试用30天”的一个需求&#xff0c;其功能主要是为了防止客户拿到产品之后不支付尾款继续使用。众所周知靠纯软件想防“盗版”&#xff0c;“限制试用”等做法是行业难题。只要价值足够高一定有人会破解绕过你的所有防线达到免费…

【开发工具之Spring Tool Suite】6、用Spring Tool Suite简化你的开发

如果你是一个喜欢用spring的人&#xff0c;你可能会在欣赏spring的强大功能外&#xff0c;对其各样的配置比较郁闷&#xff0c;尤其是相差较大的版本在配置文件方面会存在差异&#xff0c;当然你可以去花不少的时间去网上查找相关的资料&#xff0c;当你准备使用更高版本spring…

康威定律,作为架构师还不会灵活运用?

Soft skills are always hard than hard skills. 软技能比硬技能难。 老板听说最近流行“微服务”&#xff0c;问架构师咱们的系统要不要来一套&#xff1f;老板又听说最近流行“中台系统”&#xff0c;问架构师咱们要不要搞起来&#xff1f;其实&#xff0c;这些问题不用老板问…

WebView2 通过 PuppeteerSharp 实现RPA获取壁纸 (案例版)

此案例是《.Net WebView2 项目&#xff0c;实现 嵌入 WEB 页面 Chromium内核》文的续集。主要是针对WebView2的一些微软自己封装的不熟悉的API&#xff0c;有一些人已经对 PuppeteerSharp很熟悉了&#xff0c;那么&#xff0c;直接用 PuppeteerSharp的话&#xff0c;那就降低了…

[转]2022 年 Java 行业分析报告

你好&#xff0c;我是看山。 前段时间介绍了从 Java8 到 Java17 每个版本比较有特点的新特性&#xff08;收录在 从小工到专家的 Java 进阶之旅 专栏&#xff09;&#xff0c;今天看到 JRebel 发布了《2022 年 Java 发展趋势和分析》&#xff0c;于是借此分析一下 Java 行业的现…

Mysql 数据库学习笔记03 存储过程

一、存储过程&#xff1a;如下 通过 out 、inout 将结果输出&#xff0c;可以输出多个值。 * 调用存储过程&#xff1a; call 存储名称&#xff08;参数1&#xff0c;参数2&#xff0c;...&#xff09;; 如指定参数不符合要求&#xff0c;返回 Empty Set * 查询存储过…

vue+vuecli+webapck2实现多页面应用

准备工作 在本地用vue-cli新建一个项目&#xff0c;首先安装vue-cil&#xff0c;命令&#xff1a; npm install -g vue-cli 新建一个vue项目,创建一个基于"webpack"的项目,项目名为vuedemo&#xff1a; vue init webpack vuedemo 这里有一个地方需要改一下&#xff0…

一文把Docker、Kubernetes搞懂:什么是Docker?什么是Kubernetes?Docker和Kubernetes有什么关系和区别?通俗解释Docker、Kubernetes

一、Docker解决的问题 1、统一标准 ● 应用构建 ○ Java、C、JavaScript——编程各异 ○ 打成软件包 ○ .exe&#xff08;类似Windows&#xff0c;最终也只是生产exe执行&#xff09; ○ 使用docker build … 打包成 镜像——这就类似于exe ● 应用分享 ○ 所有软件的镜像放到一…

程序员双手飞快敲键盘的时候是在敲代码吗?

当你看到一个程序员的两只手在键盘上上下翻飞&#xff0c;行云流水的时候&#xff0c;多半不是在敲击代码大概率是在跟产品经理撕逼讨论需求另一种可能就是在跟测试打口水仗10%几率是在论坛码字摸鱼或者和人家开喷了。1%几率是在跟MM聊天可以手速飞快而不需要停下思考的代码&am…

几分钟上线一个网站 真是神器

1、ToolJet 简介 ToolJet 是一个开源的低代码框架&#xff0c;可以快速构建和部署内部工具&#xff0c;而无需工程团队付出太多努力。您可以连接到您的数据源&#xff0c;例如数据库&#xff08;如 PostgreSQL、MongoDB、Elasticsearch 等&#xff09;、API 端点&#xff08;To…

移植Python3到TQ2440(一)

平台 硬件&#xff1a;TQ2440 64MB内存 256MB NandFlashbootloader&#xff1a;U-Boot 2015.04kernel&#xff1a;linux-4.9Python: Python-3.6.0工具链&#xff1a;arm-none-linux-gnueabi-gcc 4.8.3概述 现在树莓派很火&#xff0c;在树莓派上面用户可以通过Python来控制板…

WinForm(六)组合布局属性

WinForm是基于控件的&#xff0c;不像codemark的架构&#xff0c;可以非常灵活的用mark来布局&#xff0c;它只能用控件布局属性和窗口控件来完成对UI的布局。容器控件有一组控件叫容器控件&#xff0c;对布局特别有作用&#xff0c;它们分别是&#xff1a;TableLayoutPanel&am…

Qt 网络编程

QT 网络编程 TCP 编程 模块引入 QT network 头文件 #include <QTcpServer> // TCP服务器端使用 #include <QTcpSocket> // TCP服务器和客户端都使用 编程流程 服务端 1&#xff09;实例化 QTcpServer 对象 -----------------------------> socket 2&#x…