2.3 Python语言基础

1 语言语义(Language Semantics)

缩进,而不是括号

Python使用空格(tabs or spaces)来组织代码结构,而不是像R,C++,Java那样用括号。

建议使用四个空格来作为默认的缩进,设置tab键为四个空格

另外可以用分号隔开多个语句:


In [1]:
a = 5; b = 6; c = 7

所有事物都是对象(object)

在python中,number,string,data structure,function,class,module都有自己的“box”,即可以理解为Python object(对象)。下面所有的对象直接用object来指代。

调用函数和对象的方法

用圆括号


In [ ]:
result = f(x, y, z)

动态参考,强类型

不像C++,Java之类的语言,python中object reference是没有自带类型的。但是可以通过type来查看类型:


In [3]:
a = 5
type(a)


Out[3]:
int

类型信息存储在这个对象本身。

而python可以看做是强类型,即每一个object都有一个明确的类型。所以下面的运算不会成立。但是Visual Basic会把'5'变为整数(int),而JavaScript会把5变为字符串(string)


In [5]:
'5' + 5


---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-5-f9dbf5f0b234> in <module>()
----> 1 '5' + 5

TypeError: Can't convert 'int' object to str implicitly

不过像是int与float之间倒是会隐式转换:


In [7]:
a = 4.5
b = 2
print('a is {0}, b is {1}'.format(type(a), type(b)))


a is <class 'float'>, b is <class 'int'>

In [8]:
a / b


Out[8]:
2.25

因为知道每个Object的类型很重要,我们可以用isinstance函数来查看object的类型


In [10]:
a = 5
isinstance(a, int)


Out[10]:
True

查看a、b是否是int或float类型


In [11]:
a = 5; b = 4.5

In [12]:
isinstance(a, (int, float))


Out[12]:
True

In [13]:
isinstance(b, (int, float))


Out[13]:
True

属性和方法

属性(Attributes)指在当前这个object里,还有一些其他的python object。方法(method)指当前这个object自带的一些函数,这些函数可以访问object里的内部数据。

通过obj.attribute_name可以查看:


In [14]:
a = 'foo'

In [15]:
a.<Press Tab>

可以通过getattr函数来访问属性和方法:


In [16]:
getattr(a, 'split')


Out[16]:
<function str.split>

Duck typing

在程序设计中,鸭子类型(英语:duck typing)是动态类型的一种风格。在这种风格中,一个对象有效的语义,不是由继承自特定的类或实现特定的接口,而是由"当前方法和属性的集合"决定。这个概念的名字来源于由James Whitcomb Riley提出的鸭子测试(见下面的“历史”章节),“鸭子测试”可以这样表述:

“当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就可以被称为鸭子。”

在鸭子类型中,关注的不是对象的类型本身,而是它是如何使用的。

比如,如果一个object能够实现迭代原则,那么这个object就是可迭代的。我们可以看这个object是否有__iter__这个magic method。或者自己写一个iter function来检测:


In [17]:
def isiterable(obj):
    try:
        iter(obj)
        return True
    except TypeError: # not iterable
        return False

In [18]:
isiterable('a string')


Out[18]:
True

In [19]:
isiterable([1, 2, 3])


Out[19]:
True

In [20]:
isiterable(5)


Out[20]:
False

这个功能多用于写一些能接受多种类型的函数。比如我们写一个函数,用接收任何序列(list, tuple, ndarray) 甚至一个迭代器。如果接收的不是一个list,那么我们就人为将其转变为一个list:


In [ ]:
if not isinstance(x, list) and isiterable(x): # 如果x不是list,且x可迭代
    x = list(x) # 转变x为list

Import(导入)

比如我创建了一个some_module.py的文件,里面写着:


In [22]:
# some_module.py PI = 3.14159

def f(x):
    return x + 2

def g(a, b):
    return a + b

那么在别的文件里,有多重导入方式:


In [ ]:
# 1
import some_module
result = some_module.f(5) 
pi = some_module.PI

# 2
from some_module import f, g, PI 
result = g(5, PI)

# 3
import some_module as sm 
from some_module import PI as pi, g as gf

r1 = sm.f(pi) 
r2 = gf(6, pi)

运算符(Binary operators)

用is,和is not, 检查两个引用(references)是否指同一个object,


In [23]:
a = [1, 2, 3]

b = a

c = list(a)

In [24]:
a is b


Out[24]:
True

In [25]:
a is not c


Out[25]:
True

因为c = list(a)中的list函数创建了一个新的list,所以c是一个新的list,不指向原来的a。

另一个is的常用法是用来检查一个instance是不是none:


In [26]:
a = None

In [27]:
a is None


Out[27]:
True

另外像是,+, - ,==, <=, &, |等都也算是运算符,这个就不详细说了,可以直接看这个链接

可更改和不可更改对象(Mutable and immutable objects)

在python的object中,lists, dicts, NumPy arrays, 以及用户自定义的类型(classes), 都是可以更改的。

而string和tuple是不可以更改的:

2 标量类型(scalar types)

这种类型指的是None,str, bytes, float, bool, int

数值型


In [28]:
ival = 123554

In [29]:
ival ** 6


Out[29]:
3557466836753811461234217695296

In [30]:
fval = 7.234
fval2 = 5.43e-5

In [34]:
5/2


Out[34]:
2.5

In [35]:
# 取商
5//2


Out[35]:
2

In [36]:
# 取余数
5 % 2


Out[36]:
1

字符串


In [41]:
a = 'one way of writing a string' 
b = "another way"
c = """ 
    This is a longer string that 
    spans multiple lines 
    """

In [43]:
c.count('\n') # 有三个回车符


Out[43]:
3

字符串类型是不可变的:


In [45]:
a[10] = 'f'


---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-45-5ca625d1e504> in <module>()
----> 1 a[10] = 'f'

TypeError: 'str' object does not support item assignment

把其他类型转换为字符串:


In [46]:
a = 5.6
s = str(a)
s


Out[46]:
'5.6'

因为字符串是一连串Unicode字符,所以可以当序列来处理,像list和tuple一样:


In [47]:
s = 'python'
list(s)


Out[47]:
['p', 'y', 't', 'h', 'o', 'n']

In [48]:
s[:3]


Out[48]:
'pyt'

反斜线用来制定特别的字符,比如回车符\n


In [50]:
s = '12\\34'
print(s)


12\34

可以用前缀r来直接写出想要的字符串格式,而不用输入很多反斜杠:


In [51]:
s = r'this\has\no\special\characters'

In [52]:
# 实际的s
s


Out[52]:
'this\\has\\no\\special\\characters'

In [54]:
# 加法用于连接两个字符串
s + s


Out[54]:
'this\\has\\no\\special\\charactersthis\\has\\no\\special\\characters'

字符串的模板,或叫做格式化,是一个很重要的课题。string ojecjt有一个format的方法:


In [56]:
template = '{0:.2f} {1:s} are worth US${2:d}'
template


Out[56]:
'{0:.2f} {1:s} are worth US${2:d}'

在这个string中:

  • {0:.2f} : 第一个参数为float类型,去小数点后两位

  • {1:s}: 把第二个参数变为string类型

  • {2:d}: 把第三个参数变为一个精确的整数


In [59]:
template.format(4.5560, 'Argentine Pesos', 1)


Out[59]:
'4.56 Argentine Pesos are worth US$1'

Bytes and Unicode

可以使用不同的编码方式


In [60]:
val = "español"
val


Out[60]:
'español'

In [62]:
val_utf8 = val.encode('utf-8')
val_utf8


Out[62]:
b'espa\xc3\xb1ol'

In [63]:
type(val_utf8)


Out[63]:
bytes

In [64]:
val_utf8.decode('utf-8')


Out[64]:
'español'

In [65]:
val.encode('latin1')


Out[65]:
b'espa\xf1ol'

In [66]:
val.encode('utf-16')


Out[66]:
b'\xff\xfee\x00s\x00p\x00a\x00\xf1\x00o\x00l\x00'

In [67]:
val.encode('utf-16le')


Out[67]:
b'e\x00s\x00p\x00a\x00\xf1\x00o\x00l\x00'

通过加一个b前缀,变为byte文字


In [68]:
bytes_val = b'this is bytes'
bytes_val


Out[68]:
b'this is bytes'

In [70]:
decoded = bytes_val.decode('utf8')
decoded # this is str (Unicode) now


Out[70]:
'this is bytes'

类型塑造(Type casting)


In [72]:
s = '3.14159'
fval = float(s)
type(fval)


Out[72]:
float

In [73]:
int(fval)


Out[73]:
3

In [74]:
bool(fval)


Out[74]:
True

In [75]:
bool(0)


Out[75]:
False

日期和时间

python内建的datetime模块提供了三种类型,datatime, date and time types:


In [76]:
from datetime import datetime, date, time

In [77]:
dt = datetime(2011, 10, 29, 20, 30, 21)

In [78]:
dt.day


Out[78]:
29

In [79]:
dt.minute


Out[79]:
30

用方法(method)提取日期或时间:


In [80]:
dt.date()


Out[80]:
datetime.date(2011, 10, 29)

In [83]:
dt.time()


Out[83]:
datetime.time(20, 30, 21)

输出string:


In [85]:
dt.strftime('%m/%d/%Y %H:%M')


Out[85]:
'10/29/2011 20:30'

把string变为datetime object:


In [86]:
datetime.strptime('20091031', '%Y%m%d')


Out[86]:
datetime.datetime(2009, 10, 31, 0, 0)

如果我们处理时间序列数据的话,把time部分换掉会比较有用,比如把minute和second变为0:


In [87]:
dt.replace(minute=0, second=0)


Out[87]:
datetime.datetime(2011, 10, 29, 20, 0)

因为datetime.datetime是不可变的,所以上面的命令是新创建了一个object。

两个不同的datetime object能产生一个datetime.timedelta类型:


In [88]:
dt2 = datetime(2011, 11, 15, 22, 30)
delta = dt2 - dt
delta


Out[88]:
datetime.timedelta(17, 7179)

In [89]:
type(delta)


Out[89]:
datetime.timedelta

这个datetime.timedelta(17, 7179)表明两个时间差17天,7179秒


In [90]:
dt


Out[90]:
datetime.datetime(2011, 10, 29, 20, 30, 21)

In [91]:
dt + delta


Out[91]:
datetime.datetime(2011, 11, 15, 22, 30)

还有其他一些datetime格式

3 控制流程 (Control Flow)

if, elif and else

for loops

while loops

上面这三个比较基础的就不介绍了,如果不懂的话可以看这个教程:控制流

pass

表示不进行任何行动,当个占位的东西


In [ ]:
if x < 0:
    print('negative!')
elif x == 0:
    # TODO: put something smart here 
    pass 
else:
    print('positive!')

range

range函数返回一个能产生序列的迭代器


In [92]:
range(10)


Out[92]:
range(0, 10)

In [93]:
list(range(10))


Out[93]:
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

In [94]:
list(range(0, 20, 2))


Out[94]:
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]

In [95]:
list(range(5, 0, -1))


Out[95]:
[5, 4, 3, 2, 1]

range的一个常用法是用来通过index迭代序列:


In [96]:
seq = [1, 2, 3, 4]
for i in range(len(seq)):
    val = seq[i]

三元表达式 Ternary expressions

value = true-expr if condition else false-expr


In [97]:
x = 5

In [98]:
'Non-negative' if x >= 0 else 'Negative'


Out[98]:
'Non-negative'

这个三元表达式通常用来简化代码,不过相对的是损失一部分可读性