热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

记录我的Python学习笔记

不想再像以前那样,什么都从头开始学习语法、总结语法,这样反而会过分纠结于语法,耽误了开发,毕竟语言的主要属性是工具ÿ

不想再像以前那样,什么都从头开始学习语法、总结语法,这样反而会过分纠结于语法,耽误了开发,毕竟语言的主要属性是工具,次要的属性是语言本身。

所以还是先熟练使用语言去进行开发,等足够熟悉了,再去研究语言本身(编译原理……)。

另外对于算法、设计模式、数据结构、网络知识、操作系统…… 的学习可以专门针对性的学习,当然更好的方法是以使用语言为主线,通过具体的应用和实践来推动这些技术知识的学习。

本文是通过廖雪峰的网站学习而整理的(真的是很好的教程,省得我花钱买书了!),然后我没有去再整理总结语法,而是直接通过写出代码段来体现自己的学习,也方便以后的快速复习、回顾。毕竟学习一门语言不是一天可以完成的,所以本文也不是一蹴而就的,而是会一直更新。

也没有必要再对代码做过多的文字解释,一切都能通过代码本身体现。

交互式命令行

在Python的交互式命令行写程序,比如>>>print('hello world'),好处是一下就能得到结果,坏处是没法保存,下次还想运行的时候,还要再敲一遍。

所以实际的开发中,我们使用一个文本编辑器来写代码,然后保存为一个文件,这样程序就可以反复运行了。比如将print('heool world')写在文档里注意print前面不能有任何空格,并保存为 hello.py,然后使用命令行进入该文件所在的目录,执行python hello.py就可以解析执行这个源文件了。

绝对不要使用windows自带的记事本:记事本会自作聪明地在文件开始的地方加上几个特殊字符(UTF-8 BOM),结果会导致程序运行出现莫名其妙的错误。

直接运行py文件

能不能像.exe文件那样直接运行.py文件呢?在Windows上是不行的,但是,在Mac和Linux上是可以的,方法是在.py文件(比如是hello.py)的第一行加上一个特殊的注释:

#!/usr/bin/env python3
print('hello, world')

接着,通过命令行给hello.py以执行权限:chmod a+x hello.py,然后就可以在文件所在目录下直接输入./hello.py运行。

这个和Shell有些像!

输入和输出

输出

>>>print('测试一个运算式', '1+2=', 1+2)
测试一个运算式 1+2= 3

注意遇到print里面将参数分开的逗号会输出空格。

输入

>>>name = input()
xumenger

当你输入完name = input()并按下回车后,Python交互式命令行就等待你的输入,以这个例子,输入xumenger,然后回车完成输入,这时候输入的字符串就存入到name变量里了。

综合输出输入的例子

编写一个test.py文件

name = input('请你输入你的姓名: ')
print('hello', name)

input里面的字符串参数作为输出提示用,然后等待输入,输入的字符串(比如xumenger)存入name,然后再输出 hello xumenger

input()print()是在命令行下面最基本的输入和输出,但是,用户也可以通过其他更高级的图形界面完成输入和输出,比如,在网页上的一个文本框输入自己的名字,点击“确定”后在网页上看到输出信息。

2015.09.06 23:40,明天开始学习Python基础,先去睡觉!

Python基础

Python语法简单,采用缩进来控制逻辑。没有规定是几个空格还是Tab,但是按照约定俗成的管理,应该始终坚持使用4个空格的缩进。在文本编辑器中,需要设置把Tab自动转换为4个空格,确保不混用Tab和空格。

# print absolute value of an integer:
a = 100
if a >= 0:print(a)
else:print(-a)

#开头的语句是注释,当语句以:结尾时,缩进的语句视为代码块。

数据类型

整数、浮点数

整数和浮点数在计算机内部存储的方式是不同的,整数运算永远是精确的(除法难道也是精确的?是的!),而浮点数运算则可能会有四舍五入的误差。

字符串(使用单引号或者双引号引起来)
如果字符串内部包含'又包含"怎么办,需要用\来转义

print('I\'m \"OK\"!')

表示:I'm "OK"!

判断

if age==18print('值为18')
elseprint('值不是18')

布尔值(True、False,用and、or和not运算)

空值

None表示,None不能理解为0,因为0是有意义的,而None是一个特殊的空值。

变量

=是赋值号,另外Python是动态语言,变量本身类型不固定。与之对应的是静态语言,静态语言在定义变量的时候必须指定变量的类型,如果赋值的时候类型不匹配,就会报错,像C、C++、Java。

a='ABC'

Python解释器解释它时,,干了两件事

  • 在内存中创建一个'ABC'的字符串

  • 在内存中创建了一个名为a的变量,并将它指向'ABC'

也可以把一个变量a赋值给另一个变量b,这个操作实际上是把变量b指向变量a所指向的数据,例如下面的代码:

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

最后一行打印出变量b的内容到底是'ABC'呢还是'XYZ'?如果从数学意义上理解,就会错误地得出b和a相同,也应该是'XYZ',但实际上b的值是'ABC'。一步一步理解代码

  • 在内存中创建 'ABC' 字符串

  • 在内存中创建 a 变量,并将 a 指向 'ABC'

  • 在内存中创建 b 变量,因为将 a 值赋给 b,所以这是b也指向 'ABC'

  • 然后又在内存中创建了 'XYZ' 字符串,并将 a 指向 'XYZ',但是此时b 还是指向 'ABC'

除法

10/3 得到的结果是 3.3333333333333
9/3 得到的结果是 3.0
10//3 得到的结果是 3
10%3 得到的结果是 1

字符编码

这个知识点以前我一直存在疑惑,廖雪峰的教程里面讲得还是很好的,点击这里认真看。重点是字符编码的原理、现在计算机系统通用的字符编码工作方式、Python中的编码方式、乱码的解释。

在最新的Python 3版本中,字符串是以 Unicode编码的,也就是说Python的字符串支持多种语言。

关于Python的字符串类型和bytes类型、在网络传输或者保存到磁盘时候字符串类型与bytes类型的转换,参考对应的廖雪峰的 字符串和编码 教程,有详细的讲解。

1个中文字符经过UTF-8编码后通常会占用3个字节,而1个英文字符只占用1个字节。

格式化

print('Hi, %s, you have $%d.' % ('loser', 10))

输出是:Hi, loser, you have $10

print('Age: %s, Gender: %s' % (25, True))

输出是:Age: 25, Gender: True

列表:list

list是一种有序的集合用[],可以随时添加和删除其中的元素

mates = ['Machael', 'Bob', 'Tracy']
print('%s' % (len(mates))
print(mates[0]) #第一个元素
print(mates[3]) #越界报错
print(mates[-1]) #倒数第一个元素
print(mates[-2]) #倒数第二个元素
print(mates[-4]) #越界报错
mates.append('xumenger') #list是可变列表,现在是在list末尾追加元素
mates.insert(1,'Joker') #插入一个元素在第二个位置
mates.pop() #删除末尾的元素
mates.pop(1) #删除第二个元素
mates[1] = 'change' #替换第2个元素L= ['test', 123, True] #list里面可以是不同类型的元素
s= ['test', 1, ['asp', 2], True] #list的元素可以是另一个list
list1 = ['test', True]
list2 = [2, False, list1]
print(list2[2][0]) #可以看成是一个二维数组,类似的还有三维、四维……数组,不过很少用到。

元组:tuple

tuple和list很相似,但是tuple是一旦初始化就不能再修改的,用()

mates= ('xumeng', 'joker', 'test')

现在,mates这个tuple不能变了,它也没有append(),insert()这样的方法。其他获取元素的方法和list是一样的,你可以正常地使用mates[0],mates[-1],但不能赋值成另外的元素。

不可变的tuple有什么意义?因为tuple不可变,所以代码更安全。如果可能,能用tuple代替list就尽量用tuple。

t=(1)定义的不是tuple,是1这个数!这是因为括号()既可以表示tuple,又可以表示数学公式中的小括号,这就产生了歧义,因此,Python规定,这种情况下,按小括号进行计算,计算结果自然是1。

要想定义只有一个元素的tuple,应该这样t=(1,)

“可变的tuple”,可以是tuple有一个list元素,然后里面的list可变,可以看教程中 对应部分 通过展示内存中的存储原理来说明原因:

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

条件判断

a = input('输入你的年龄: ')
age = int(a) #因为input输入的是字符串,所以要转换成整型
if age >= 18: #注意冒号 :print('adult, your age is', age)
elif age >= 6: #注意冒号 :print('teenager, your age is', age)
else: #注意冒号 :print('kid')

循环

names = ['nn', 'aa', 'mm']
for name in names: #注意冒号 :

print(name)

sum = 0
for x in[1, 2, 3, 4, 5]

sum = sum +x

print(sum)

sum = 0
for x in range(101): #range(101)就可以生成0-100的整数序列

sum = sum + x

print(sum)

sum = 0
n = 99
while n>0: #注意冒号:

sum = sum +n
n= n-2

print(sum)

字典:dict

Python内置了字典:dict的支持,全称为dictionary,在其他语言中也成为map,使用键-值(key-value)存储,具有几块的查找速度,注意使用{}

d = {'key1': 95, 'key2': 54, 'key3': 100}
print(d['key1'])
#把数据放入dict的方法,除了初始化时指定外,还可以通过key放入
d['key4'] = 123
#由于一个key只能对应一个value,所以,多次对一个key放入value,后面的值会把前面的值冲掉
d['key1'] = 111if 'key2' in d: #判断某个键是不是在dict中print('在')if get('key2', -1) == -1: #如果有'key2'则获取对应的value,否则就返回-1print('不在')d.pop('key3') #使用pop删除对应的键和值

为什么dict查找速度这么快?因为dict的实现原理和查字典是一样的。假设字典包含了1万个汉字,我们要查某一个字,一个办法是把字典从第一页往后翻,直到找到我们想要的字为止,这种方法就是在list中查找元素的方法,list越大,查找越慢。

第二种方法是先在字典的索引表里(比如部首表)查这个字对应的页码,然后直接翻到该页,找到这个字。无论找哪个字,这种查找速度都非常快,不会随着字典大小的增加而变慢。

你可以猜到,这种key-value存储方式,在放进去的时候,必须根据key算出value的存放位置,这样,取的时候才能根据key直接拿到value。

请务必注意,dict内部存放的顺序和key放入的顺序是没有关系的。

dict可以用在需要高速查找的很多地方,在Python代码中几乎无处不在,正确使用dict非常重要,需要牢记的第一条就是dict的key必须是不可变对象。

这是因为dict根据key来计算value的存储位置,如果每次计算相同的key得出的结果不同,那dict内部就完全混乱了。这个通过key计算位置的算法称为哈希算法(Hash)。

要保证hash的正确性,作为key的对象就不能变。在Python中,字符串、整数等都是不可变的,因此,可以放心地作为key。而list是可变的,就不能作为key。

set

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

#要创建一个set,需要提供一个list作为输入结合
s1 = set([1, 2, 3])
#重复元素在set中被过滤,比如下面的语句,其实只要1,2,3
s= set([1, 1, 2, 2, 2, 3, 3])
#通过add(key)添加元素,重复添加的元素只会保留一个
s.add(4)
#remove(key) 删除元素
s.remove(1,2,3)
#set可以看成数学意义上的无序和无重复元素的集合,因此,两个set可以做数学意义上的交集、并集等操作
s2 = set([1,2,3])
s3 = set([2,3,4])
s4 = s2 & s3 #s4是s2和s3的交集
s5 = s2 | s3 #s4是s2和s3的并集

set和dict的唯一区别仅在于没有存储对应的value,但是,set的原理和dict一样,所以,同样不可以放入可变对象,因为无法判断两个可变对象是否相等,也就无法保证set内部“不会有重复元素”。试试把list放入set,看看是否会报错。

廖雪峰的 讲解dict和set的文章 的最后通过说明内存里面的原理讲解了可变对象与不可变对象!很好的理解Python和内存机制的一个知识点!

tuple虽然是不变对象,但试试把(1, 2, 3)和(1, [2, 3])放入dict或set中,并解释结果。

s1 = set([(1,2,3), (2,3,4)]) #这样的tuple可以放进set
s2 = set([(1,2, [1,2]), (2,3, [6,8])]) #这样的tuple不能放进set,这是“可变的tuple”

2015.09.07 23:45, 明天开始学习 Python的函数 ,现在赶紧睡觉,身体最重要!

函数

调用函数

要调用一个函数,需要知道函数的名称和参数,比如求绝对值的函数abs,只有一个参数。可以直接从Python的官方网站查看文档:http://docs.python.org/3/library/functions.html#abs

也可以在交互式命令行通过help(abs)查看abs函数的帮助信息。

print(abs(-1))
print(max(1,2,3,4))
print(max(1,2,3))
print(int('123')) #强制类型转换
print(float('12.34))
print(str(100))
print(bool(1)) #输出True
print(bool('')) #输出False

函数名其实是一个指向函数对象的引用,完全可以把一个函数名赋值给一个变量,相当于给这个函数起了一个别名:

a=abs
print(a(-1))
m=max
print(max(1,2,3))

定义函数

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

def my_abs(x):if not isinstance(x, (int, float)):raise TypeError('bad operand type')if x>=0:return xelse:return -x

上面的函数中,对参数类型做检查,只允许整数和浮点数类型的参数,否则就raise一个异常。如果有必要,可以先对参数的数据类型做检查,就像这个函数定义,就可以保证只处理int和float,而假如传入的是str就会抛出异常。

请注意,函数体内部的语句在执行时,一旦执行到return时,函数就执行完毕,并将结果返回。因此,函数内部通过条件判断和循环可以实现非常复杂的逻辑。

如果没有return语句,函数执行完毕后也会返回结果,只是结果为None。return None可以简写为return。

import mathdef move(x, y, step, angle=0):nx = x + step * math.cos(angle)ny = y - step * math.sin(angle)return nx, ny

import math语句表示导入math包,并允许后续代码引用math包里的sin、cos等函数。上面的这个函数有两个返回值,我们可以这样调用

x, y = move(100, 100, 60, math, pi/6)
print(x, y)

但是,其实这个只是假象,Python函数返回的仍然是单一值:

r = move(100, 100, 60, math.pi / 6)
print(r) #得到和上面一样的结果

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

Python强大的函数参数

廖雪峰的函数的参数 这一章讲解了位置参数、默认参数、因为参数类型不是不变对象导致使用默认参数出现的"意外"、list和tuple与可变参数、dict与关键字参数、命名关键参数。

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

这个太他妈强大了!!但是有点没看懂,确实纯粹的死记硬背还是不太有效,可以等到具体项目应用的时候在参考这篇教程!结合具体的应用再来深入的理解,绝对事半功倍,现在就需要知道Python中有很强大的函数参数的语法,等到具体用的时候知道到哪里去找相关的资料就行了!

递归函数

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

使用递归函数需要注意防止栈溢出。在计算机中,函数调用是通过栈(stack)这种数据结构实现的,每当进入一个函数调用,栈就会加一层栈帧,每当函数返回,栈就会减一层栈帧。由于栈的大小不是无限的,所以,递归调用的次数过多,会导致栈溢出。可以试试fact(1000)。

解决递归调用栈溢出的方法是通过尾递归优化,具体应用见廖雪峰的递归函数 的教程。

尾递归调用时,如果做了优化,栈不会增长,因此,无论多少次调用也不会导致栈溢出。遗憾的是,大多数编程语言没有针对尾递归做优化,Python解释器也没有做优化,所以,即使把上面的fact(n)函数改成尾递归方式,也会导致栈溢出。

高级特性

掌握了Python的数据类型、语句和函数,基本上就可以编写出很多有用的程序了。

比如构造一个1, 3, 5, 7, ..., 99的列表,可以通过循环实现:

L = []
n = 1
while n <&#61; 99:L.append(n)n &#61; n &#43; 2

取list的前一半的元素&#xff0c;也可以通过循环实现。

但是在Python中&#xff0c;代码不是越多越好&#xff0c;而是越少越好。代码不是越复杂越好&#xff0c;而是越简单越好。用任何的语言编程都应该是这样。

基于这一思想&#xff0c;我们来介绍Python中非常有用的高级特性&#xff0c;1行代码能实现的功能&#xff0c;决不写5行代码。请始终牢记&#xff0c;代码越少&#xff0c;开发效率越高。

切片

L &#61; [&#39;Michael&#39;, &#39;Sarah&#39;, &#39;Tracy&#39;, &#39;Bob&#39;, &#39;Jack&#39;]
L1 &#61; L[:3] #[&#39;Michael&#39;, &#39;Sarah&#39;, &#39;Tracy&#39;]
L2 &#61; L[1:3] #[&#39;Sarah&#39;, &#39;Tracy&#39;]
L3 &#61; L[-2:] #[&#39;Bob&#39;, &#39;Jack&#39;]
L4 &#61; L[-2:-1] #[&#39;Bob&#39;]
#list的第一个元素的索引是0&#xff0c;倒数第一个元素的索引是-1LL&#61;list(range(100)) #[1,2,3,...,99]
LL1&#61;L[-10:] #[90,91,...,99] 后10个数
LL2&#61;L[10:20] #[10,11,12,...,19] 前11-20个数
LL3&#61;L[:10:2] #[0,2,4,6,8] 前10个数&#xff0c;每两个取一个
LL4&#61;L[::5] #[0,5,10,...,90,95] 所有数&#xff0c;每5个取一个
LL5&#61;L[:] #甚至什么都不写&#xff0c;只写[:]就可以原样复制一个list

tuple也是一种list&#xff0c;唯一区别是tuple不可变。因此&#xff0c;tuple也可以用切片操作&#xff0c;只是操作的结果仍是tuple。

T&#61;(0,1,2,3,4,5)
T1&#61;T[:3]

字符串&#39;xxx&#39;也可以看成是一种list&#xff0c;每个元素就是一个字符。因此&#xff0c;字符串也可以用切片操作&#xff0c;只是操作结果仍是字符串&#xff1a;

T&#61;&#39;ABCDEFG&#39;
T1&#61;T[:3] #&#39;ABC&#39;
T2&#61;T[::2] #&#39;ACEG&#39;

在很多编程语言中&#xff0c;针对字符串提供了很多各种截取函数&#xff08;例如&#xff0c;substring&#xff09;&#xff0c;其实目的就是对字符串切片。Python没有针对字符串的截取函数&#xff0c;只需要切片一个操作就可以完成&#xff0c;非常简单。

有了切片操作&#xff0c;很多地方循环就不再需要了。Python的切片非常灵活&#xff0c;一行代码就可以实现很多行循环才能完成的操作。

迭代

Python的for循环抽象程度要高于Java的for循环&#xff0c;因为Python的for循环不仅可以用在list或tuple上&#xff0c;还可以作用在其他可迭代对象上。

list这种数据类型虽然有下标&#xff0c;但很多其他数据类型是没有下标的&#xff0c;但是&#xff0c;只要是可迭代对象&#xff0c;无论有无下标&#xff0c;都可以迭代&#xff0c;比如dict就可以迭代。

d&#61; {&#39;a&#39;:1, &#39;b&#39;:2, &#39;c&#39;:3}
for key in d:print(key)
#输出a c b

为什么输出的结果是a c b&#xff0c;不是a b c&#xff0c;因为dict的存储不是按照list的方式顺序排列&#xff0c;所以&#xff0c;迭代出的结果顺序很可能不一样。关于dict的存储的知识&#xff0c;请参见对应的dict教程。

默认情况下&#xff0c;dict迭代的是key。如果要迭代value&#xff0c;可以用for value in d.values()&#xff0c;如果要同时迭代key和value&#xff0c;可以用for k, v in d.items()

由于字符串也是可迭代对象&#xff0c;所以可以用于for循环。

for ch in &#39;ABCD&#39;:print ch

所以&#xff0c;当我们使用for循环时&#xff0c;只要作用于一个可迭代对象&#xff0c;for循环就可以正常运行&#xff0c;而我们不太关心该对象究竟是list还是其他数据类型。那么&#xff0c;如何判断一个对象是可迭代对象呢&#xff1f;方法是通过collections模块的Iterable类型判断&#xff1a;

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

最后一个小问题&#xff0c;如果要对list实现类似Java那样的下标循环怎么办&#xff1f;Python内置的enumerate函数可以把一个list变成索引-元素对&#xff0c;这样就可以在for循环中同时迭代索引和元素本身&#xff1a;

for i, value in enumerate([&#39;A&#39;, &#39;B&#39;, &#39;C&#39;]):print(i, value)

上面的for循环里&#xff0c;同时引用了两个变量&#xff0c;在Python里是很常见的&#xff0c;比如下面的代码&#xff1a;

for x, y in [(1, 1), (2, 4), (3, 9)]:print(x, y)

列表生成式

L&#61;list(range(1,11)) #生成[1,2,3,4,5,6,7,8,9,10]
L1&#61;[x*x for x in range(1,11)] #生成[1*1,2*2,...,10*10]

写列表生成式时&#xff0c;把要生成的元素比如x * x放到前面&#xff0c;后面跟for循环&#xff0c;就可以把list创建出来&#xff0c;十分有用&#xff0c;多写几次&#xff0c;很快就可以熟悉这种语法。

for循环后面还可以加上if判断&#xff0c;这样我们就可以筛选出仅偶数的平方&#xff1a;

L&#61; [x * x for x in range(1, 11) if x % 2 &#61;&#61; 0] #[4, 16, 36, 64, 100]

还可以使用两层循环&#xff0c;可以生成全排列&#xff1a;

L&#61; [m &#43; n for m in &#39;ABC&#39; for n in &#39;XYZ&#39;]
#[&#39;AX&#39;, &#39;AY&#39;, &#39;AZ&#39;, &#39;BX&#39;, &#39;BY&#39;, &#39;BZ&#39;, &#39;CX&#39;, &#39;CY&#39;, &#39;CZ&#39;]

三层和三层以上的循环就很少用到了。

例程&#xff0c;列出当前目录下的所有文件和目录名

import os # 导入os模块&#xff0c;模块的概念后面讲到
L&#61;[d for d in os.listdir(&#39;.&#39;)] # os.listdir可以列出文件和目录
print(L)

for循环其实可以同时使用两个甚至多个变量&#xff0c;比如dict的items()可以同时迭代key和value&#xff1a;

d &#61; {&#39;x&#39;: &#39;A&#39;, &#39;y&#39;: &#39;B&#39;, &#39;z&#39;: &#39;C&#39; }
for k, v in d.items():print(k, &#39;&#61;&#39;, v)

因此&#xff0c;列表生成式也可以使用两个变量来生成list&#xff1a;

d &#61; {&#39;x&#39;: &#39;A&#39;, &#39;y&#39;: &#39;B&#39;, &#39;z&#39;: &#39;C&#39; }
L&#61; [k &#43; &#39;&#61;&#39; &#43; v for k, v in d.items()]

把一个list中所有的字符串变成小写&#xff1a;

L &#61; [&#39;Hello&#39;, &#39;World&#39;, &#39;IBM&#39;, &#39;Apple&#39;]
L1&#61; [s.lower() for s in L]

生成器

通过列表生成式&#xff0c;我们可以直接创建一个列表。但是&#xff0c;受到内存限制&#xff0c;列表容量肯定是有限的。而且&#xff0c;创建一个包含100万个元素的列表&#xff0c;不仅占用很大的存储空间&#xff0c;如果我们仅仅需要访问前面几个元素&#xff0c;那后面绝大多数元素占用的空间都白白浪费了。

所以&#xff0c;如果列表元素可以按照某种算法推算出来&#xff0c;那我们是否可以在循环的过程中不断推算出后续的元素呢&#xff1f;这样就不必创建完整的list&#xff0c;从而节省大量的空间。在Python中&#xff0c;这种一边循环一边计算的机制&#xff0c;称为生成器&#xff1a;generator。

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

L &#61; [x * x for x in range(10)]
print(L)
#[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
g &#61; (x * x for x in range(10)) #()是一个generator
print(g)
# at 0x1022ef630>

我们可以直接打印出list的每一个元素&#xff0c;但我们怎么打印出generator的每一个元素呢&#xff1f;如果要一个一个打印出来&#xff0c;可以通过next()函数获得generator的下一个返回值。我们讲过&#xff0c;generator保存的是算法&#xff0c;每次调用next(g)&#xff0c;就计算出g的下一个元素的值&#xff0c;直到计算到最后一个元素&#xff0c;没有更多的元素时&#xff0c;抛出StopIteration的错误。

但是&#xff0c;如果每次输出都调用next(g)实在是太变态了&#xff0c;正确的方法是使用for循环&#xff0c;因为generator也是可迭代对象&#xff1a;

g &#61; (x*x for x in range(10))
for n in gprint(n)

所以&#xff0c;我们创建了一个generator后&#xff0c;基本上永远不会调用next()&#xff0c;而是通过for循环来迭代它&#xff0c;并且不需要关心StopIteration的错误。

generator非常强大。如果推算的算法比较复杂&#xff0c;用类似列表生成式的for循环无法实现的时候&#xff0c;还可以用函数来实现。这篇对应的教程中还讲了更为牛逼的generator的使用方法&#xff01;

迭代器

我们已经知道&#xff0c;可以直接作用于for循环的数据类型有以下几种&#xff1a;

一类是集合数据类型&#xff0c;如list、tuple、dict、set、str等&#xff1b;

一类是generator&#xff0c;包括生成器和带yield的generator function。

这些可以直接作用于for循环的对象统称为可迭代对象&#xff1a;Iterable。

可以使用isinstance()判断一个对象是否是Iterable对象&#xff1a;

from collections import Iterable
isinstance([], Iterable) #True
isinstance({}, Iterable) #True
isinstance(&#39;abc&#39;, Iterable) #True
isinstance((x for x in range(10)), Iterable) #True
isinstance(100, Iterable) #False

而生成器不但可以作用于for循环&#xff0c;还可以被next()函数不断调用并返回下一个值&#xff0c;直到最后抛出StopIteration错误表示无法继续返回下一个值了。

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

可以使用isinstance()判断一个对象是否是Iterator对象&#xff1a;

from collections import Iterator
isinstance((x for x in range(10)), Iterator) #True
isinstance([], Iterator) #False
isinstance({}, Iterator) #False
isinstance(&#39;abc&#39;, Iterator) #False

生成器都是Iterator对象&#xff0c;但list、dict、str虽然是Iterable&#xff0c;却不是Iterator。

把list、dict、str等Iterable变成Iterator可以使用iter()函数&#xff1a;

isinstance(iter([]), Iterator) #True
isinstance(iter(&#39;abc&#39;), Iterator) #True

你可能会问&#xff0c;为什么list、dict、str等数据类型不是Iterator&#xff1f;

这是因为Python的Iterator对象表示的是一个数据流&#xff0c;Iterator对象可以被next()函数调用并不断返回下一个数据&#xff0c;直到没有数据时抛出StopIteration错误。可以把这个数据流看做是一个有序序列&#xff0c;但我们却不能提前知道序列的长度&#xff0c;只能不断通过next()函数实现按需计算下一个数据&#xff0c;所以Iterator的计算是惰性的&#xff0c;只有在需要返回下一个数据时它才会计算。

Iterator甚至可以表示一个无限大的数据流&#xff0c;例如全体自然数。而使用list是永远不可能存储全体自然数的。

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

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

Python的for循环本质上就是通过不断调用next()函数实现的&#xff0c;例如&#xff1a;

for x in [1, 2, 3, 4, 5]:pass

实际上完全等价于&#xff1a;

# 首先获得Iterator对象:
it &#61; iter([1, 2, 3, 4, 5])
# 循环:
while True:try:# 获得下一个值:x &#61; next(it)except StopIteration:# 遇到StopIteration就退出循环break
函数式编程

函数式编程的一个特点就是&#xff0c;允许把函数本身作为参数传入另一个函数&#xff0c;还允许返回一个函数&#xff01;

Python对函数式编程提供部分支持。由于Python允许使用变量&#xff0c;因此&#xff0c;Python不是纯函数式编程语言。

高阶函数

那么函数名是什么呢&#xff1f;函数名其实就是指向函数的变量&#xff01;对于abs()这个函数&#xff0c;完全可以把函数名abs看成变量&#xff0c;它指向一个可以计算绝对值的函数&#xff01;

如果把abs指向其他对象&#xff0c;会有什么情况发生&#xff1f;

abs &#61; 10
abs(-10)

报错&#xff1a;
Traceback (most recent call last):
File "", line 1, in
TypeError: &#39;int&#39; object is not callable

把abs指向10后&#xff0c;就无法通过abs(-10)调用该函数了&#xff01;因为abs这个变量已经不指向求绝对值函数而是指向一个整数10&#xff01;

当然实际代码绝对不能这么写&#xff0c;这里是为了说明函数名也是变量。要恢复abs函数&#xff0c;请重启Python交互环境。

由于abs函数实际上是定义在__builtin__模块中的&#xff0c;所以要让修改abs变量的指向在其它模块也生效&#xff0c;要用__builtin__.abs &#61; 10。

既然变量可以指向函数&#xff0c;函数的参数能接收变量&#xff0c;那么一个函数就可以接收另一个函数作为参数&#xff0c;这种函数就称之为高阶函数。例子

def add(x, y, f):return f(x) &#43; f(y)

可以这样调用&#xff1a;

add(-5, 6, abs)

编写高阶函数&#xff0c;就是让函数的参数能够接收别的函数。函数式编程就是指这种高度抽象的编程范式。

map/reduce

我们先看map。map()函数接收两个参数&#xff0c;一个是函数&#xff0c;一个是Iterable&#xff0c;map将传入的函数依次作用到序列的每个元素&#xff0c;并把结果作为新的Iterator返回。

def f(x):return x*x
r &#61; map(f, [1,2,3,4,5,6,7,8,9])
print(list(r)) #[1,4,16,25,36,49,64,81]

再看reduce的用法。reduce把一个函数作用在一个序列[x1, x2, x3, ...]上&#xff0c;这个函数必须接收两个参数&#xff0c;reduce把结果继续和序列的下一个元素做累积计算&#xff0c;其效果就是&#xff1a;

reduce(f, [x1, x2, x3, x4]) &#61; f(f(f(x1, x2), x3), x4)

例子&#xff0c;比方说对一个序列求和&#xff0c;就可以用reduce实现&#xff1a;

from functools import reduce
def add(x, y):

return x &#43; y

print(reduce(add, [1, 3, 5, 7, 9])) #输出25

这个例子本身没多大用处&#xff0c;但是&#xff0c;如果考虑到字符串str也是一个序列&#xff0c;对上面的例子稍加改动&#xff0c;配合map()&#xff0c;我们就可以写出把str转换为int的函数&#xff1a;

from functools import reduce
def fn(x, y):return x * 10 &#43; ydef char2num(s):return {&#39;0&#39;: 0, &#39;1&#39;: 1, &#39;2&#39;: 2, &#39;3&#39;: 3, &#39;4&#39;: 4, &#39;5&#39;: 5, &#39;6&#39;: 6, &#39;7&#39;: 7, &#39;8&#39;: 8, &#39;9&#39;: 9}[s]print(reduce(fn, map(char2num, &#39;13579&#39;))) #输出13579

整理成一个str2int的函数就是&#xff1a;

from functools import reducedef str2int(s):def fn(x, y):return x * 10 &#43; ydef char2num(s):return {&#39;0&#39;: 0, &#39;1&#39;: 1, &#39;2&#39;: 2, &#39;3&#39;: 3, &#39;4&#39;: 4, &#39;5&#39;: 5, &#39;6&#39;: 6, &#39;7&#39;: 7, &#39;8&#39;: 8, &#39;9&#39;: 9}[s]return reduce(fn, map(char2num, s))

还可以用lambda函数进一步简化成&#xff1a;

from functools import reducedef char2num(s):return {&#39;0&#39;: 0, &#39;1&#39;: 1, &#39;2&#39;: 2, &#39;3&#39;: 3, &#39;4&#39;: 4, &#39;5&#39;: 5, &#39;6&#39;: 6, &#39;7&#39;: 7, &#39;8&#39;: 8, &#39;9&#39;: 9}[s]def str2int(s):return reduce(lambda x, y: x * 10 &#43; y, map(char2num, s))

也就是说&#xff0c;假设Python没有提供int()函数&#xff0c;你完全可以自己写一个把字符串转化为整数的函数&#xff0c;而且只需要几行代码&#xff01;

filter

Python内建的filter()函数用于过滤序列。

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

例如&#xff0c;在一个list中&#xff0c;删掉偶数&#xff0c;只保留奇数&#xff0c;可以这么写&#xff1a;

def is_odd(n):return n % 2 &#61;&#61; 1list(filter(is_odd, [1, 2, 4, 5, 6, 9, 10, 15]))
# 结果: [1, 5, 9, 15]

可见用filter()这个高阶函数&#xff0c;关键在于正确实现一个“筛选”函数。

注意到filter()函数返回的是一个Iterator&#xff0c;也就是一个惰性序列&#xff0c;所以要强迫filter()完成计算结果&#xff0c;需要用list()函数获得所有结果并返回list。

sorted

排序也是在程序中经常用到的算法。无论使用冒泡排序还是快速排序&#xff0c;排序的核心是比较两个元素的大小。如果是数字&#xff0c;我们可以直接比较&#xff0c;但如果是字符串或者两个dict呢&#xff1f;直接比较数学上的大小是没有意义的&#xff0c;因此&#xff0c;比较的过程必须通过函数抽象出来。通常规定&#xff0c;对于两个元素x和y&#xff0c;如果认为x y&#xff0c;则返回1&#xff0c;这样&#xff0c;排序算法就不用关心具体的比较过程&#xff0c;而是根据比较结果直接排序。

Python内置的sorted()函数就可以对list进行排序&#xff1a;

sorted([36, 5, -12, 9, -21]) #[-21, -12, 5, 9, 36]

此外&#xff0c;sorted()函数也是一个高阶函数&#xff0c;它还可以接收一个key函数来实现自定义的排序&#xff0c;例如按绝对值大小排序&#xff1a;

sorted([36, 5, -12, 9, -21], key&#61;abs) #[5, 9, -12, -21, 36]
#key指定的函数将作用于list的每一个元素上&#xff0c;并根据key函数返回的结果进行排序。

我们再看一个字符串排序的例子&#xff1a;

sorted([&#39;bob&#39;, &#39;about&#39;, &#39;Zoo&#39;, &#39;Credit&#39;])
#[&#39;Credit&#39;, &#39;Zoo&#39;, &#39;about&#39;, &#39;bob&#39;]

默认情况下&#xff0c;对字符串排序&#xff0c;是按照ASCII的大小比较的&#xff0c;由于&#39;Z&#39; <&#39;a&#39;&#xff0c;结果&#xff0c;大写字母Z会排在小写字母a的前面。

现在&#xff0c;我们提出排序应该忽略大小写&#xff0c;按照字母序排序。要实现这个算法&#xff0c;不必对现有代码大加改动&#xff0c;只要我们能用一个key函数把字符串映射为忽略大小写排序即可。忽略大小写来比较两个字符串&#xff0c;实际上就是先把字符串都变成大写&#xff08;或者都变成小写&#xff09;&#xff0c;再比较。

这样&#xff0c;我们给sorted传入key函数&#xff0c;即可实现忽略大小写的排序&#xff1a;

sorted([&#39;bob&#39;, &#39;about&#39;, &#39;Zoo&#39;, &#39;Credit&#39;], key&#61;str.lower)
#[&#39;about&#39;, &#39;bob&#39;, &#39;Credit&#39;, &#39;Zoo&#39;]

要进行反向排序&#xff0c;不必改动key函数&#xff0c;可以传入第三个参数reverse&#61;True&#xff1a;

sorted([&#39;bob&#39;, &#39;about&#39;, &#39;Zoo&#39;, &#39;Credit&#39;], key&#61;str.lower, reverse&#61;True)
#[&#39;Zoo&#39;, &#39;Credit&#39;, &#39;bob&#39;, &#39;about&#39;]

从上述例子可以看出&#xff0c;高阶函数的抽象能力是非常强大的&#xff0c;而且&#xff0c;核心代码可以保持得非常简洁。

返回函数

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

我们来实现一个可变参数的求和。通常情况下&#xff0c;求和的函数是这样定义的&#xff1a;

def calc_sum(*args):ax &#61; 0for n in args:ax &#61; ax &#43; nreturn ax

但是&#xff0c;如果不需要立刻求和&#xff0c;而是在后面的代码中&#xff0c;根据需要再计算怎么办&#xff1f;可以不返回求和的结果&#xff0c;而是返回求和的函数&#xff1a;

def lazy_sum(*args):def sum():ax &#61; 0for n in args:ax &#61; ax &#43; nreturn axreturn sum

当我们调用lazy_sum()时&#xff0c;返回的并不是求和结果&#xff0c;而是求和函数&#xff1a;

f &#61; lazy_sum(1, 3, 5, 7, 9)
f

.sum at 0x101c6ed90>

调用函数f时&#xff0c;才真正计算求和的结果&#xff1a;

f() #25

闭包

注意到返回的函数在其定义内部引用了局部变量args&#xff0c;所以&#xff0c;当一个函数返回了一个函数后&#xff0c;其内部的局部变量还被新函数引用&#xff0c;所以&#xff0c;闭包用起来简单&#xff0c;实现起来可不容易。

另一个需要注意的问题是&#xff0c;返回的函数并没有立刻执行&#xff0c;而是直到调用了f()才执行。我们来看一个例子&#xff1a;

def count():fs &#61; []for i in range(1, 4):def f():return i*ifs.append(f)return fsf1, f2, f3 &#61; count()

在上面的例子中&#xff0c;每次循环&#xff0c;都创建了一个新的函数&#xff0c;然后&#xff0c;把创建的3个函数都返回了。

你可能认为调用f1()&#xff0c;f2()和f3()结果应该是1&#xff0c;4&#xff0c;9&#xff0c;但实际结果是&#xff1a;

f1() #9
f2() #9
f3() #9

全部都是9&#xff01;原因就在于返回的函数引用了变量i&#xff0c;但它并非立刻执行。等到3个函数都返回时&#xff0c;它们所引用的变量i已经变成了3&#xff0c;因此最终结果为9。

返回闭包时牢记的一点就是&#xff1a;返回函数不要引用任何循环变量&#xff0c;或者后续会发生变化的变量。

如果一定要引用循环变量怎么办&#xff1f;方法是再创建一个函数&#xff0c;用该函数的参数绑定循环变量当前的值&#xff0c;无论该循环变量后续如何更改&#xff0c;已绑定到函数参数的值不变&#xff1a;

def count():def f(j):def g():return j*jreturn gfs &#61; []for i in range(1, 4):fs.append(f(i)) # f(i)立刻被执行&#xff0c;因此i的当前值被传入f()return fs

再看看结果&#xff1a;

f1, f2, f3 &#61; count()
f1() #1
f2() #4
f3() #9

缺点是代码较长&#xff0c;可利用lambda函数缩短代码。

匿名函数

当我们在传入函数时&#xff0c;有些时候&#xff0c;不需要显式地定义函数&#xff0c;直接传入匿名函数更方便。

在Python中&#xff0c;对匿名函数提供了有限支持。还是以map()函数为例&#xff0c;计算f(x)&#61;x2时&#xff0c;除了定义一个f(x)的函数外&#xff0c;还可以直接传入匿名函数&#xff1a;

list(map(lambda x: x * x, [1, 2, 3, 4, 5, 6, 7, 8, 9]))
#[1, 4, 9, 16, 25, 36, 49, 64, 81]

通过对比可以看出&#xff0c;匿名函数lambda x: x * x实际上就是&#xff1a;


def f(x):return x * x

关键字lambda表示匿名函数&#xff0c;冒号前面的x表示函数参数。

匿名函数有个限制&#xff0c;就是只能有一个表达式&#xff0c;不用写return&#xff0c;返回值就是该表达式的结果。

用匿名函数有个好处&#xff0c;因为函数没有名字&#xff0c;不必担心函数名冲突。此外&#xff0c;匿名函数也是一个函数对象&#xff0c;也可以把匿名函数赋值给一个变量&#xff0c;再利用变量来调用该函数&#xff1a;

f &#61; lambda x: x * x
f
# at 0x101c6ef28>
f(5) #25

同样&#xff0c;也可以把匿名函数作为返回值返回&#xff0c;比如&#xff1a;


def build(x, y):return lambda: x * x &#43; y * y

装饰器

def log(func):def wrapper(*args, **kw):print(&#39;call %s():&#39; % func.__name__)return func(*argc, **kw)return wrapper

观察上面的log&#xff0c;因为它是一个decorator&#xff0c;所以接受一个函数作为参数&#xff0c;并返回一个函数。我们要借助Python的&#64;语法&#xff0c;把decorator置于函数的定义处&#xff1a;


&#64;log
def now():print(&#39;2015-3-25&#39;)

调用now()函数&#xff0c;不仅会运行now()函数本身&#xff0c;还会在运行now()函数前打印一行日志&#xff1a;

now()
#call now():
#2015-3-25

把&#64;log放到now()函数的定义处&#xff0c;相当于执行了语句&#xff1a;


now &#61; log(now)

由于log()是一个decorator&#xff0c;返回一个函数&#xff0c;所以&#xff0c;原来的now()函数仍然存在&#xff0c;只是现在同名的now变量指向了新的函数&#xff0c;于是调用now()将执行新函数&#xff0c;即在log()函数中返回的wrapper()函数。

wrapper()函数的参数定义是(args, *kw)&#xff0c;因此&#xff0c;wrapper()函数可以接受任意参数的调用。在wrapper()函数内&#xff0c;首先打印日志&#xff0c;再紧接着调用原始函数。

在面向对象&#xff08;OOP&#xff09;的设计模式中&#xff0c;decorator被称为装饰模式。OOP的装饰模式需要通过继承和组合来实现&#xff0c;而Python除了能支持OOP的decorator外&#xff0c;直接从语法层次支持decorator。Python的decorator可以用函数实现&#xff0c;也可以用类实现。

decorator可以增强函数的功能&#xff0c;定义起来虽然有点复杂&#xff0c;但使用起来非常灵活和方便。

偏函数

functools.partial就是帮助我们创建一个偏函数的&#xff0c;不需要我们自己定义int2()&#xff0c;可以直接使用下面的代码创建一个新的函数int2&#xff1a;

import functools
int2 &#61; functools.partial(int, base&#61;2)
print(int2(&#39;1000000&#39;)) #64
print(int2(&#39;1010101&#39;)) #85

Python的functools模块提供了很多有用的功能&#xff0c;其中一个就是偏函数&#xff08;Partial function&#xff09;。要注意&#xff0c;这里的偏函数和数学意义上的偏函数不一样。

没太看懂&#xff0c;还是等到具体研究一个项目源码&#xff0c;以及自己做开发的时候再去结合实践深入理解吧&#xff01;

2015.09.08 23:59&#xff1a;00 明天继续看 模块 的教程&#xff0c;今天对很多知识点并没有真正理解&#xff0c;都是有一些印象&#xff0c;所以必须等到自己研究源码、自己开发的时候&#xff0c;结合运行的效果和理论知识去达到真正的深入的理解。现在赶紧睡觉&#xff01;

模块

在计算机程序的开发过程中&#xff0c;随着程序代码越写越多&#xff0c;在一个文件里代码就会越来越长&#xff0c;越来越不容易维护。

为了编写可维护的代码&#xff0c;我们把很多函数分组&#xff0c;分别放到不同的文件里&#xff0c;这样&#xff0c;每个文件包含的代码就相对较少&#xff0c;很多编程语言都采用这种组织代码的方式。在Python中&#xff0c;一个.py文件就称之为一个模块&#xff08;Module&#xff09;。

引入了包以后&#xff0c;只要顶层的包名不与别人冲突&#xff0c;那所有模块都不会与别人冲突。现在&#xff0c;abc.py模块的名字就变成了mycompany.abc&#xff0c;类似的&#xff0c;xyz.py的模块名变成了mycompany.xyz。

请注意&#xff0c;每一个包目录下面都会有一个__init__.py的文件&#xff0c;这个文件是必须存在的&#xff0c;否则&#xff0c;Python就把这个目录当成普通目录&#xff0c;而不是一个包。__init__.py可以是空文件&#xff0c;也可以有Python代码&#xff0c;因为__init__.py本身就是一个模块&#xff0c;而它的模块名就是mycompany。

类似的&#xff0c;可以有多级目录&#xff0c;组成多级层次的包结构。

使用模块

安装第三方模块

很多强大的第三方库&#xff0c;要能够充分利用好它们为我服务&#xff01;&#xff01;&#xff01;

面向对象编程

面向对象编程——Object Oriented Programming&#xff0c;简称OOP&#xff0c;是一种程序设计思想。OOP把对象作为程序的基本单元&#xff0c;一个对象包含了数据和操作数据的函数。

面向过程的程序设计把计算机程序视为一系列的命令集合&#xff0c;即一组函数的顺序执行。为了简化程序设计&#xff0c;面向过程把函数继续切分为子函数&#xff0c;即把大块函数通过切割成小块函数来降低系统的复杂度。

而面向对象的程序设计把计算机程序视为一组对象的集合&#xff0c;而每个对象都可以接收其他对象发过来的消息&#xff0c;并处理这些消息&#xff0c;计算机程序的执行就是一系列消息在各个对象之间传递。

2015.09.22&#xff0c;明天继续学习《面向对象编程》

比如一个类的代码如下

class Student(object):def __init__(self, name, score):self.name &#61; nameself.score &#61; scoredef print_score(self):print(&#39;%s: %s&#39; % (self.name, self.score))

可以这样使用这个类创建对象

bart &#61; Student(&#39;Bart Simpson&#39;, 59)
lisa &#61; Student(&#39;Lisa Simpson&#39;, 87)
bart.print_score()
lisa.print_score()

面向对象的设计思想是从自然界中来的&#xff0c;因为在自然界中&#xff0c;类&#xff08;Class&#xff09;实例&#xff08;Instance&#xff09;的概念是很自然的。Class是一种抽象概念&#xff0c;比如我们定义的Class——Student&#xff0c;是指学生这个概念&#xff0c;而实例&#xff08;Instance&#xff09;则是一个个具体的Student&#xff0c;比如&#xff0c;Bart Simpson和Lisa Simpson是两个具体的Student。

所以&#xff0c;面向对象的设计思想是抽象出Class&#xff0c;根据Class创建Instance。

面向对象的抽象程度又比函数要高&#xff0c;因为一个Class既包含数据&#xff0c;又包含操作数据的方法。

数据封装、继承和多态是面向对象的三大特点&#xff0c;我们后面会详细讲解。

类和实例

关于面向对象设计其实和C&#43;&#43;、Delphi……都很像&#xff0c;但是具体的语法可能不同&#xff0c;不过这都是一些表面化的东西。具体去参考Python的编程规范、语法就好了。

这篇教程里面有关于类、实例、实例的内存地址……的讲解&#xff0c;所以要好好看看&#xff01;

__init__方法是类的构造方法&#xff0c;self这个特殊变量的理解。

和普通的函数相比&#xff0c;在类中定义的函数只有一点不同&#xff0c;就是第一个参数永远是实例变量self&#xff0c;并且&#xff0c;调用时&#xff0c;不用传递该参数。除此之外&#xff0c;类的方法和普通函数没有什么区别&#xff0c;所以&#xff0c;你仍然可以用默认参数、可变参数、关键字参数和命名关键字参数。

要定义一个方法&#xff0c;除了第一个参数是self外&#xff0c;其他和普通函数一样。要调用一个方法&#xff0c;只需要在实例变量上直接调用&#xff0c;除了self不用传递&#xff0c;其他参数正常传入。

因为Python是静态语言&#xff0c;所以语法上还会有其他更多的区别&#xff0c;所以一定要和其他的之前我了解的语言在语法方面区分开

访问限制

一些关于变量的权限、访问限制、命名规范的说明。总的来说就是&#xff0c;Python本身没有任何机制阻止你干坏事&#xff0c;一切全靠自觉。

继承和多态

在继承关系中&#xff0c;如果一个实例的数据类型是某个子类&#xff0c;那它的数据类型也可以被看做是父类。但是&#xff0c;反过来就不行。可以使用isistance()函数来进行判断。

这篇教程很好的讲解了多态的表现形式&#xff01;&#xff01;具体的编程语法、代码实现的细节&#xff0c;认真参考这篇教程&#xff01;&#xff01;

获取对象信息

type()

isinstance()

dir()&#xff1a;如果要获得一个对象的所有属性和方法&#xff0c;可以使用dir()函数&#xff0c;它返回一个包含字符串的list&#xff0c;比如&#xff0c;获得一个str对象的所有属性和方法。



推荐阅读
author-avatar
手机用户2502905797
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有