在之前对Python的印象,也仅仅是停留在口嗨阶段,也就是一直说要学要看,但是从来没看过也没学过。自己明白Python现在比较火,然后知道大家大多数人都觉得Python入手快,并且机器学习和人工智能等等领域的框架都更青睐Python。
但实际上自己从来没有去认真学过Python。自己虽然很菜,但是在大多数情况下基本上是能用Java就用Java。首先是因为用一个自己熟悉的编程语言更顺手,并且自己比较懒,多多少少有点局限于舒适圈中无法自拔了。
今天突然决定要学习一下Python了。因为我自己惊喜的发现我们学校的课表上,我们下学期的课居然有Python程序设计基础这门课。我再当懒狗我怕是要挂科了。
首先我自己觉得学编程语言对比学习是一个很正常的事,但是我非常讨厌学编程语言硬要套到另一个语言上的学习方式。比如学Java应要把Java的类和C语言的结构体非要做个类比。所以我感觉学习的时候应当得去发现以下Python的新鲜事物,而不是非要带入一个已有的思维去分析,即使非要这样做,也应该是站在自己对这个东西全面了解的基础之上才有资格去做的。
写这篇文章的目的有:
- 图个乐,身为菜鸡,写技术文章也就是为了图个乐;
- 装个逼,记录一下自己今天看Python都看了啥,内容堆在一起看着有成就感,显得自己学的很多,其实也没多到哪里去;
- 做笔记,记性不好,万一哪天忘了个啥,能回顾一下,再去看教程翻书或者看视频太麻烦。
一、基本语法
我个人认为常见的这些编程语言中,基本的语法是有相通之处的,所以这里我拿我比较熟悉的Java来做个类比,看看究竟有什么异同之处。
1.1 表示格式
首先要说的是注释,Python里没有多行注释的概念,单行注释的前缀符是#
。
其次要说的是“代码块的表示”。
在Java里经常用{
和}
括起来表示一段代码,每行代码都有个分号表示结束。
Python里头不兴这个,Python用代码缩进表示代码块,经过我的粗略观察,大多数人一个缩进用四个空格表示。这有时候就很草,尤其表现在抄别人代码的时候,复制过来必须得仔细检查缩进,说不定哪个地方缩进错了会有个什么小毛病。
1.2 基本的数据类型
由于我太菜了,我对这些数据类型的认识只能停留在一个“我只是看看”的程度,深究一波的话有点困难。所以这里我决定简单看一下这些数据类型,先挨个打个招呼。
数字类型
哪个编程语言还能没个数字类型呢。在Python里头数字类型有int和float,表示整数和浮点数。在Python3中删掉了Python2中的long。Python没有double,并且在Python里新增了复数Complex。
Complex的玩法其实很简单。他的表述很数学形式化。
- 取实部和虚部:例如
4+3j
就是一个复数。(4+3j).real
可以取实部,(4+3j).imag
可以取虚部。 - 如果不深究的话,可以直接认为Python特殊优化了一个单位
j
,这个j
可以跟数字做加和,计算出一个新的复数。 - 复数的实部和虚部是浮点数。
布尔值(bool)
布尔值表示起来有True
和False
。这里还是熟悉的味道。
布尔运算肯定少不了它的布尔运算符。在Python里常用的布尔运算符有and
、or
和not
。
注意Python是一门区分大小写的语言,所以这些单词的大小写是不能弄错的,比如true
就不对,True
才能算对,同理AND
就不对,and
才是对的。
字符串(str)
Python中字符串操作是很灵活的。字符串可以用单引号或者双引号括起来。Python里没有字符这一数据类型,如果提到了字符这个东西,通常就是在说只有一个字符的字符串。
我们可以用字符串数组的思想 (这绝不是说str是字符串数组) 来去看待str这一数据类型,所以可以用str[0]
取到这个字符串的第一个字符。
如果字符串里需要插入特殊字符,可以跟Java等语言一样使用转义。但是在Python里提供了一种“关闭转移”的符号标志 r
。
例如 r'\t'
表示的就是纯粹的 \t
这一个由\
字符和t
字符一共两个字符组成的字符串。
Python里对字符串的\n
做了一个特殊优化。使用'''
(即三个单引号)也能表示字符串的起始和终止。这样做可以肆意换行表示\n
。
例如:
'''Hi!!!
Hello!!!'''
这玩意儿的意思就等效于'Hi!!!\nHello!!!'
。这种多行的表示方式有时候很方便。
这里有个有意思的地方。有的人说Python是有多行注释的。其实就是利用了这里的字符串可以弄多行表示的方式,可以利用'''
拟出多行注释的效果2333333
空值(None)
None
的地位就是null
,这是老朋友了。
Python里的数字是没有一个绝对的“数据范围”的限制的。但是特殊对浮点数而言,如果数字足够的大,会被表示为inf
(无穷大). inf
不是一种数据类型,跟nan一样是一种记号而已。
Python里有个很有意思的语句叫pass
,表示占个位置,啥事也不干。
列表(list)
Pyhon里没有数组。但是Python里有列表。
列表是一种有序的集合,它可以方便的添加和删除元素。可以把它跟Java的List
做个小类比(还是List<Object>
的那种)。
一个list里存的数据不一定是要同一种数据类型,里面的各个元素数据类型可以是不一样的。也就是说如果想在Python里搞类似多维数组的东西,那其实就是list里存list。
这里不得不提及一下Python的编号方式。
首先是最传统的从0开始的下标编号。这没啥说的,从头往后数的。
然后是“倒数”,-1
表示倒数第一个。也就是说,Python的下标体系在通常情况下是有“倒数”下标这么个操作的。
元组(tuple)
元组是一种常量,我感觉这就跟离散里的元组的概念是一样的。
但是有个理解上的问题。我们不可以把元组想象成跟C语言结构体一样的东西。为什么呢?C语言结构体定义出来的数据,每个位置上的元素还能改。元组不一样,元组是常量。
这里有点绕,我决定给自己举个例子(虽然可能看完后感觉更绕了)。比如数学上有个点的坐标是(1,1),现在小明就在这上面,我把他挪到(2,3)了。这个时候我应该理解为小明把自己的坐标从(1,1)这个元组改为了(2,3)这个元组,而不应当理解为我把1改成了2,把另一个1改成了3,这是不对的,如果真这么理解,那我修改的是这个点表示的坐标值,这很奇怪。
元组里可以是没有元素的,表示为()
。一个特殊规定,如果是一元组,必须加个逗号,比如(1,)
,而不能写成(1)
,这应该是防止跟数字类型的运算起冲突。其他的就无所谓了,比如(2,3)
啥的。
这里还有个小细节。假如我想弄一个二维坐标点的加法运算这么一个东西,我的正确思路应该是用复数实现,而不应该是元组,因为(1,2)+(3,4)
的结果是(1,2,3,4)
。
字典(dict) 和 集合(set)
dict
就是Map
,set
就是Set
,这倒没有啥亮点。
dict
里,如果想取元素可以d['Jack']
这样取,但是一旦'Jack'
不存在就报错了。可以用d.get('Jack)
这个方法来取,这样返回的是None
,不会报错。
对于set
,取交集可以用运算符|
,取并集可以用&
。
1.3 基本数据类型的理解
Python是一门面向对象的语言,在Python里,像Java里的基本数据类型和类定义的数据类型的区分是被同一且模糊化了的。int
是由int
类定义的,其他数据类型同理。
这也就意味着,基本的这些数据类型都有他们对应的方法可以调用。
并且Python里的每个基本数据类型的运算符都有其对应的实体函数,所以可以通过重载函数的方式来实现类似“重写运算符”的目的。
由于这些原因,Python的基本数据类型的使用方法极其灵活。这还需要后面自己慢慢探索。
这里有个疑惑的地方。为什么我既要说“方法”,又要说“函数”。如果按照Java的思想来看待,所有代码都在类里,那自然不应当存在“函数”和“方法”这两个称呼并存这么一件事才对。
Python里并不是所有的代码都只能写在class里,也可以在py文件里。定义在py文件里的就是函数,在class里的那些就是方法了。也就是根本上是定义在了哪里的问题。
二、结构设计
2.1 变量和常量
Python里没有常量这个东西。常量说白了就是没人去改的变量。
Python里的变量也不需要提前事先定义,变量的类型是动态变化的。我个人觉得Python里的变量可以理解为一种代号的概念,它可以现在表示int,一会儿表示float,将来表示str,而不是卡死的。
2.2 条件判断和循环
经常可以听到“顺序程序设计”、“条件程序设计”和“循环程序设计”的说法,那Python里的这些概念现在都是这么样子呢?
首先是if
条件判断。用法如下:
age = 3
if age >= 18:
print('adult')
elif age >= 6:
print('teenager')
else:
print('kid')
注意这些地方的冒号不要忘了。唯一需要记住的地方就是elif
,这里Python整了个简写,其他的没啥,该是啥就是啥。
然后是循环。Python里有for
和while
两种循环。
首先是while循环。while循环的用法还是熟悉的味道,看一下就行。
n = 0
while n < 10:
n = n + 1
if n % 2 == 0:
continue
print(n)
for循环只有for-each的用法。但是如果希望迭代循环 [0,10) 这10个数字的话,可以用range
函数生成一个可迭代对象,表示这十个数字。就像这样:
for a in range(0,10): # 如果是从0开始的话,可以写range(10)
print(a)
2.3 定义函数
最基本的方法
看一下就知道了:
def my_abs(x):
if x >= 0:
return x
else:
return -x
判断参数类型
有个细节。因为传进来的x是个变量,他啥数据类型都是有可能的,所以有时候必须要判断一下他的数据类型,这个时候可以用isinstance
函数。
def my_abs(x):
if not isinstance(x, (int, float)):
raise TypeError('bad operand type')
if x >= 0:
return x
else:
return -x
我感觉这里有个贼帅的地方值得一说。isinstance
函数传进去了两个参数,第一个是变量值,第二个是个元组。python里可以用类名表示类传进去。
返回多个值
由于Python里面有元组这个概念,所以函数可以看上去像是返回了两个数据。
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) # 像不像返回了俩值
但其实这是在这里的一个特殊写法。其实这里是返回了tuple。如果只是返回了这么一个tuple,可以省略括号,接的时候可以写对应数量的变量,所以就有了这么个用法。
可变参数
参数还可以是可变参数,就像是接收到了一个tuple一样,这样写:
def calc(*numbers):
sum = 0
for n in numbers:
sum = sum + n * n
return sum
还可以是关键字参数,就像这样:
def person(name, age, **kw): #kw当dist处理
print('name:', name, 'age:', age, 'other:', kw)
# 就像这样传参
person('Adam', 45, gender='M', job='Engineer')
# 如果本来就有个dist,那可以这么玩来调用
extra = {'city': 'Beijing', 'job': 'Engineer'}
person('Jack', 24, **extra)
如果要限制关键字参数的名字,就可以用命名关键字参数,例如,只接收city和job作为关键字参数。这种方式定义的函数如下:
def person(name, age, *, city, job):
print(name, age, city, job)
和关键字参数 **kw
不同,命名关键字参数需要一个特殊分隔符,后面的参数被视为命名关键字参数。
调用方式如下:
person('Jack', 24, city='Beijing', job='Engineer')
如果函数定义中已经有了一个可变参数,后面跟着的命名关键字参数就不再需要一个特殊分隔符*
了.
def person(name, age, *args, city, job):
print(name, age, args, city, job)