本文是 Python 机器学习基础系列文章的第一篇——Python 篇。
Python
任何一门编程语言,入门学习的基础知识包括:数据类型、控制流、函数、模块化、类,以及一些常用的零碎语法。Python 亦不例外。
数据类型
基本数据类型
包括布尔型(bool)、整型(int)、长整型(long)、浮点型(float)、复数(complex)五种。
- 内置常量:
False
,True
,None
,NotImplemented
,Ellipsis
,__debug__
布尔型(bool):只有两个值
True
和False
,每个 Python 对象都天生具有布尔值,None
,False
,0
,0L
,0.0
,0.0+0.0j
,''
,[]
,()
,{}
值为False
,自定义类在方法__nonzero__()
或__len__()
中返回布尔值,此外其他对象的布尔值都为True
,在数值运算中True
和False
值为1
和0
,布尔操作包括and, or, not, !=, is, is not
整型(int):等价于 C 语言中的
long
类型,与系统能表示的最大整型一致,包括十进制、八进制(数字0
开头)、十六进制(0x
或0X
开头),可通过bin
,oct
,hex
转换进制,可进行/
,//
,**
等运算长整型(long):是整型的超集,可以表示无限大的整数,在整数后加
L
或l
来表示如a = 999 ** 8
,pow(2L, 5L)
等浮点型(float):等价于 C 中的
double
,其精度信息和内部表示可以从sys.float_info
中获得,可以用十进制或科学计数法(1e-2
等)表示复数(complex):
c = 08.333-1.47j
,c.real
,c.imag
,c.conjugate()
,abs(c)
容器
容器是数据的集合。Python 的容器,除字符串(string)外,主要包括四种:元组(tuple),列表(list),字典(dict)和集合(set)。其中,字典和集合是无序容器,而元组、列表和字符串都是有序容器,支持索引(index)和切片(slicing)操作。此外,字符串、元组和集合是不可变容器,而列表和字典是可变容器。不可变容器通常比可变容器(如 list)更为高效。
下面介绍这五种容器:
字符串(str):使用单引号(
'hello'
)、双引号("hello"
)或三引号('''hello'''
)定义。三引号可定义多行字符串。包含startswith
等方法。元组(tuple):圆括号与逗号分隔定义(也可以不带括号),如
t = (0, "two", 3.0, "four", (5,6))
。必须在定义时初始化且内容和长度不可变。0 个元素的元组这么定义a = ()
,1 个元素的元组这么定义a = (2 ,)
,元素可以是不同类型。tuple 也常用来打包一组变量如t = (i, s, f)
,ii, ss, ff = t
。print
函数%
后的部分其实就是元组,如print '%s is %d years old' % (name, age)
。列表(list):方括号与逗号分隔定义,如
l = [1, (2,3), "hello"]
。也可以通过list
函数来构造,此时需要传入一个元组或字符串,如l = list((1,2,3)), a = list("hello")
。list 与 tuple 的区别在于其内容和长短可变,通过append
来添加元素,通过del
命令删除,如del l[0]
。list 包含append
,sort
,pop
,count
,reverse
等成员函数。字典(dict):花括号与逗号分隔来定义,如
d = { 'name': 'John', 'age': 18, 'feat': (1,3,'tall')}
,每个字典项用冒号隔开 key 与 value。可用索引操作符[]
来寻址一个键,或者对其赋值,也可用del d['key']
将某项删除。集合(set):集合是无序不重复元素集,通过
set()
函数来定义,传入一个字符串、元组、列表或字典,如s = set([1, 2, "hello"])
,传入字符串时构建不重复字符集合,传入字典时默认用 keys 构建集合。集合支持并集(|
)、交集(&
)、差集(-
)、对称差集(^
)运算符,包含add
,update
,remove
,issubset
,isuperset
,copy
,clear
等子函数。
索引与切片
索引(index):序列容器(tuple, list, string, unicoe, frozenset)都可以通过索引操作符
[]
来存取元素。索引从 0 开始,也可以是负数,表示倒数第几个元素。如t[0], t[-1], t[-2]
分别表示第一个、最后一个与倒数第二个元素。切片(slice):切片是同时取容器内某个索引范围的多个元素。形式为
t[start:end:step]
,注意end
是结束位置索引 + 1
,如t[0:-3:2]
。可以省略三个值里的一个或多个,如a[2:7]
,l[0::2]
,t[:-1]
等。连接:序列容器(string, tuple, list)的加法(
+
)表示序列的连接而非容器求和。如a = (1, 2, 3) + (4, 5, 6)
则a
的值为(1, 2, 3, 4, 5, 6)
。序列容器的乘法(*
,只能乘以非负整数)表示重复多次。此外,可以用extend
成员函数来连接两个序列容器。
其他语法
强制类型转换(工厂函数)
如 bool(5.0)
, int(5.0)
, complex(1, 2.5)
, float(5)
, long(5.0)
, str(1.24)
自动类型转换
- 操作数有一个是复数,另一个也被转成复数
- 否则,有一个是浮点数,另一个也被转换成浮点数
- 否则,如果有一个是长整数,另一个也被转换成长整数
- 否则,两者必然都是整型,无需转换
coerce(x, y)
函数:将变量x
和y
按照上述规则转换,并返回转换后的x
和y
组成的元组
辅助函数
如 len()
可获得元组、列表等的长度,type()
可获得变量类型,help()
可获取函数帮助,id()
可查看对象或函数的唯一标识符
表达式与控制流
运算符
同样有 +
, -
, *
, /
,此外 **
表示幂,//
表示取整除(只取商的整数部分),%
求余数,~
表示按位翻转,!=
表示不等于,位运算符有 &
, |
, ^
, ~
, <<
, >>
等,not
, and
和 or
分别表示布尔“与”、“非”、“或”。
in
, not in
是成员运算符,用于成员测试,is
, is not
是标识运算符,用于同一性测试,
控制流
控制流改变语句的执行顺序。包括条件(if-elif-else)、循环(while-else, for-in-else)、异常(try-except-finally)和上下文管理器(with)。
Python 用 :
操作符来新开一个语句块,并且新的语句块通过缩进和结束缩进来表示语句块的开始和结束。if, for, while, else, try, except, def 等都是如此。
条件语句
条件语句 if 涉及的关键词包括:elif, else, pass, continue, break。其中 pass 表示不需要任何操作。
if not x: pass
elif x > 100:
print 'big number'
else:
print 'small number'
循环语句
循环语句 while 与 for 和其他编程语言类似,不同点在于 while 和 for 后可选择性地接 else 语句,当循环正常结束时会执行 else 里的动作,而当循环通过 break 跳出时会跳过 else 里的代码。此外 for 循环语句的形式为 for x in s
。
i = 0
while i < 10:
print i
if i % 5 == 0:
break
i += 1
else:
print 'The else statement'
for i in range(5):
print i
for i,x in numerate(s):
print i,':',x
for x,y,z in s:
print x,y,z
异常
try:
raise RuntimeError("error")
except RuntimeError as e:
pass
except (IOError, TypeError, NameError) as e:
pass
其中 RuntimeError 是异常类型,raise 语句用于抛出异常,在 except 中捕获异常。如果在异常出现后必须进行某些动作,用 finally 语句,finally 里的内容,无论异常是否发生,都会执行。此外 try 也支持 else 子句,表示当异常没有发生时要做什么事情。
try:
f = open('hello.txt')
except IOError as e:
pass
else:
data = f.read()
finally:
f.close()
上下文管理器
Python 中用 with 语句来表示上下文管理器,上下文管理器可以正确地管理各种资源,不需要时自动释放资源,如文件的打开和关闭,线程锁等等。
with open('hello.txt', 'r') as f:
f.read()
import threading
lock = threading.Lock()
with lock:
pass
执行 with obj
时,它会先执行 obj.__enter__()
函数来进入一个新的上下文,离开则调用 obj.__exit__(type, value, traceback)
方法。该方法返回一个布尔值,指示被引发的异常是否得到处理。
with obj
后接受一个可选的 as var
说明符,obj.__enter()__
的返回值将保存在 var 中。有了这些,我们可以自己定义一个可以 with 的对象:
class MyList():
def __enter__(self):
self.theList = list()
return self.theList
def __exit__(self, type, value, tb):
for i in self.theList:
print i
return False
with MyList() as l:
l.append(10)
l.append('woman')
l.append('man')
零碎语法
- 注释:单行注释用
# your comment
,多行注释用''' your comments '''
- Python 里的所有赋值只会复制其引用而非对象本身,修改会相互影响(注意重新赋值不算修改)。要完整复制,必须使用切片或
copy
成员函数操作符来拷贝 print
语句末尾添加逗号,
可以阻止换行c = [i for i in a]
可以转换为一个列表,c = {i for i in a}
可以转换为一个集合- 使用继续字符(
\
)可以在多行分割语句 - Python 可以用
A <= x <= B
来比较变量范围 - Python 支持多点赋值:
x, y, z = 15, 10, 7
,因此交换两个变量可以使用x, y = y, x
a == b
用于比较两个变量值是否相等,a is b
用于检查两个名字是否引用同一个对象print
函数可接收多个参数,会自动在输入之间加上空格del
任意一个对象时,实际上是删除了其对对象的引用,仅当引用计数为零时才真正删除指向的数据dir()
函数可以列出当前命名空间下的所有变量和函数dir
函数可以列出一个模块中的所有子模块,如import math; dir(math)
a[::-1]
形式的索引可以反转一个序列容器
函数
普通函数
通过 def 来定义,如:
def printme( str ):
print str
return
printme('hello')
按引用传递参数
所有参数在 Python 里都是按引用传递。如果你在函数里修改了参数(注意重新赋值不算修改,因为重新赋值其实会删除原始引用,并新建指向新值的引用),原始参数也会被改变。如:
def changeme( mylist ):
mylist.append([1,2,3,4])
print mylist
return
mylist = [10,20,30]
changeme( mylist )
print mylist
会输出 [10,20,30,[1,2,3,4]]
。
参数类型
包括必备参数、关键字参数、默认(缺省)参数和不定长参数四种。
def printinfo(name, age = 35):
print "Name:", name
print "Age:", age
return
printinfo(50, 'chris')
printinfo(age=50, name='chris')
其中 name 是必备参数,age 是缺省参数,printinfo(age=50, name='chris')
是以关键字方式传参。
不定长参数定义方式如下:
def functionname([formal_args,] *var_args_tuple ):
code_here
return [expression]
加了星号(*)的变量名会存放所有未命名的变量参数。如:
def printstr(arg1, *vartuple):
print arg1
for var in vartuple:
print var
return
printstr(10)
return 语句
函数最后用 return [expression] 形式(选择性地)返回一个或多个结果。如:
def maxmin(l):
return max(l), min(l)
返回多个结果时,实际上是把多个结果打包在一个元组里返回。
匿名函数
Python 使用 lambda 表达式来创建匿名函数。
lambda 表达式实际上用于定义一个闭包,有自己的命名空间,切不能访问自有参数以外的全局参数。lambda 只是一个表达式,函数体比 def 简单很多,只能在其中封装有限的逻辑进去。lambda 函数定义形式如下:
lambda [arg1 [,arg2,...,argn]]:expression
如下实例:
sum = lambda arg1, arg2: arg1 + arg2;
print "10 + 20 = ", sum(arg1, arg2)
模块
C 语言中想要调用 sqrt 函数时必须通过 #include <math.h>
引入相关头文件。同样,在 Python 中,如果需要引用一些内置或写在其它文件里的函数,需要用到一个概念叫做模块(module)。Python 通过 import 关键字来引入模块。
import math
print math.sqrt(2)
调用模块中的函数时,必须通过 模块名.函数名
的形式。如果只需要用到某几个函数,可以通过 from 模块名 import 函数1, 函数2
的形式。当多个模块包含同一个函数时,后引用的模块会覆盖掉前面的模块。
在 Python 中,每个 Python 文件都可以作为一个模块,模块名就是文件名。在 Python 引入模块时实际上是将对应文件中的代码执行一遍(仅在第一次引入时才会执行一遍)。
我们可以测试一下。新建一个 test.py 文件,包含如下代码:
# test.py
def display():
print 'hello python'
display()
再编写一个 test1.py 文件,输入:
import test
import test
会发现输出 hello python
,且仅输出一次。所以首次 import 一个模块时实际上是执行一遍对应文件中的代码。
类
Pythohn 在设计之初就定义为一门面向对象的语言。正因如此,在 Python 中创建一个类或对象是非常容易的事情。
Python 面向对象技术的关键词有:
- 类(class):具有相同属性和方法的对象集合。类定义了集合中每个对象所共有的属性和方法,对象是类的实例。
- 类变量:类变量独立于对象存在,在不同实例中是公用的。类变量通常不作为实例变量使用。
- 实例变量:定义在方法之内,只作用于当前实例。
- 数据成员:类变量或者实例变量,也叫字段,用于存储类或实例对象的相关数据。
- 方法:类中定义的函数。
- 实例化:创建一个类的实例,类的具体对象。
- 对象:通过类定义的数据结构实例,对象包括类变量和实例变量以及方法。
- 继承:即一个派生类(derived class)继承基类(basis class)的字段和方法。
- 方法重写:如果从父类继承的方法不能满足子类需求,可以在子类中对其进行改写,这个过程叫方法的覆盖(override),也叫重写。
类的定义
通常你需要在单独的文件中定义一个类。
class Employee:
empCount = 0
def __init__(self, name, salary):
self.name = name
self.salary = salary
Employee.empCount += 1
def displayCount(self):
print 'Total employee %d' % Employee.empCount
def displayEmployee(self):
print 'Name :", self.name, ", Salary :", self.salary
def __del__(self):
class_name = self.__class__.__name__
print class_name, "is destroyed"
emp = Employee("Chris", 10000)
上例中通过 class
关键字定义了一个类 Employee,其中 empCount 就是类变量,在所有对象之间共享,而 name 和 salary 就是实例变量,为某个实例独有。__init__
是 Python 的一种特殊方法,叫构造函数或初始化方法,在创建实例时会调用。而 __del__
是析构函数,在对象被销毁时被调用。
注意上述所有方法的第一个参数都是 self,表示定义类内方法时都需要把实例本身的引用传进来。
你可以添加、修改或删除类的属性:
emp.school = 'BIT'
emp.age = 7
del emp.age
也可以通过如下函数来访问或检查实例的属性:
hasattr(emp, 'age')
getattr(emp, 'age')
setattr(emp, 'age', 8)
delattr(emp, 'age')
Python 的内置类属性有:
__name__
:类名__doc__
:类的文档,即类定义下方的注释__dict__
:类和实例都有,包含一个字典,由类的数据属性组成__module__
:类定义所在的模块__bases__
:类的所有父类组成的元组
Python 垃圾回收
与 Java 语言一样,Python 使用了引用计数这一简单规则来追踪内存中的对象。
a = 40 # 创建对象 <40>
b = a # 增加对 <40> 引用的计数,为 2
c = [b] # 增加对 <40> 引用的计数,为 3
del a # 减少对 <40> 引用的计数,为 2
b = 100 # 减少对 <40> 引用的计数,为 1
c[0] = -1 # 减少对 <40> 引用的计数,为 0,删除数据
除引用计数以外,Python 还会处理循环引用的情况。如两个对象相互引用,但没有其他变量引用它们,这种情况也需要清理,但仅仅引用计数是不够的。Python 的垃圾回收机制实际上是一个引用计数和一个循环垃圾收集器。
类的继承
Python 中继承的一些特点:
- 子类不会自动调用基类的构造函数(
__init__
),需要显式地调用。 - 调用基类的方法时,需要加上基类的类名前缀,且要加上 self 参数,区别于类中调用普通函数时并不需要带上 self 参数。
- 现在本类中查找方法,找不到再去基类中找。
语法:
class SubClassName (ParentClass1[, ParentClass2, ...]):
'Optional class documentation string'
class_suite
直接在子类中定义与父类同名的函数即可重写方法。一些通用的功能包括 __init__
, __del__
, __repr__
, __str__
(转化为适合人阅读的形式), __cmp__
(对象比较)可以在自己的类中重写,通过重写 __add__
, __len__
, __lt__
, __eq__
, __iter__
等可以实现运算符的重载。
私有属性与方法
Python 类中的数据成员与方法默认为公有。通过在字段或方法名前添加两个下划线如 __var
来定义私有属性或方法。如下实例:
class Counter:
__secretCount = 0 # 私有变量
publicCount = 0 # 公开变量
def count(self):
self.__secretCount += 1
self.publicCount += 1
print self.__secretCount
counter = Counter()
counter.count()
counter.count()
print counter.publicCount
print counter.__secretCount # 报错,实例不能访问私有变量
但是,你可以使用 object.__className__attrName
访问属性(我靠,这设计,真奇葩!),如:
print counter._Counter__secretCount
装饰器
Python 特点
Python 与其他语言数据类型的比较
编程语言之间比较:
静态类型语言
一种在编译期间就确定数据类型的语言。大多数静态类型语言是通过要求在使用任一变量之前声明其数据类型来保证这一点的。Java 和 C 是静态类型语言。动态类型语言
一种在运行期间才去确定数据类型的语言,与静态类型相反。VBScript 和 Python 是动态类型的,因为它们确定一个变量的类型是在您第一次给它赋值的时候。强类型语言
一种总是强制类型定义的语言。Java 和 Python 是强制类型定义的。您有一个整数,如果不明确地进行转换 ,不能将把它当成一个字符串。弱类型语言
一种类型可以被忽略的语言,与强类型相反。VBScript 是弱类型的。在 VBScript 中,您可以将字符串 ‘12’ 和整数 3 进行连接得到字符串’123’,然后可以把它看成整数 123 ,所有这些都不需要任何的显示转换。
所以说 Python 既是动态类型语言 (因为它不使用显示数据类型声明),又是强类型语言 (因为只要一个变量获得了一个数据类型,它实际上就一直是这个类型了)。(本段摘自这里)