python 速成指南

python 速成指南

作者:doudehou@gmail.com

第一节. 过程式 python

python 的一个特点是不通过大括号 {} 来划定代码块,而是通过缩进。如果和 C/C++ 类比的话,就是在左括号的地方不要换行,然后用一个冒号 (:) 替代, C/C++ 大括号内部的东西,缩进一个 tab 或者几个空格都可以(但需要保持一致),比如:

if (x < 2):

   print 'x < 2'

elif (x > 2):

   print 'x > 2'

else:

   print 'bingo!'

print 'x = 2'

注意两点:一是 python 语句结尾处没有分号(;)作为结束标记。二是和 C/C++ 不同,没有 else if,而是用 elif 替代,相当于可以少打几个字符吧。

 (语句的分隔

在C、Java等语言的语法中规定,必须以分号作为语句结束的标识。Python也支持分号,同样用于一条语句的结束标识。但在Python中分号的作用已经不像C、Java中那么重要了,Python中的分号可以省略,主要通过换行来识别语句的结束

例如,以下两行代码是等价的:

  1. print "hello world!" 
  2. print "hello world!"

第1行代码的输出结果:

  1. hello world! 

第2行代码的输出结果:

  1. hello world! 

如果要在一行中书写多条句,就必须使用分号分隔每个语句,否则Python无法识别语句之间的间隔:

  1. # 使用分号分隔语句  
  2. x=1; y=1 ; z=1 

第2行代码有3条赋值语句,语句之间需要用分号隔开。如果不隔开语句,Python解释器将不能正确解释,提示语法错误:

  1. SyntaxError: invalid syntax 

注意分号不是Python推荐使用的符号,Python倾向于使用换行符作为每条语句的分隔,简单直白是Python语法的特点。通常一行只写一条语句,这样便于阅读和理解程序。一行写多条语句的方式是不好的习惯。

Python同样支持多行写一条语句,Python使用“\\”作为换行符。在实践中,一条语句写在多行也是非常常见的。

【例2-17】把SQL语句作为参数传递给函数,由于SQL的语句一般非常长,为了阅读方便,因此需要换行书写。

  1. # 字符串的换行  
  2. # 写法一  
  3. sql = "select id,name \\  
  4. from dept \\  
  5. where name = 'A'"  
  6. print sql  
  7. # 写法二  
  8. sql = "select id,name " \\  
  9. "from dept " \\  
  10. "where name = 'A'" 
  11. print sql 

写法一只使用了一对双引号,把SQL语句分为select、from、where等3部分分别书写。

第6行代码输出结果:

  1. select id,name from dept where name = 'A' 

写法二使用了3对双引号,select、from、where分别对应一对双引号。

第11行代码输出结果:

  1. select id,name from dept where name = 'A' 

第二种写法比第一种写法的可读性更强,可以使用空格和制表符对齐语句,使代码显得更工整。对于简短的语句不推荐换行的写法,这种写法只会造成阅读的复杂性。下面这段程序是不合理的换行写法:

  1. # 一条语句写在多行  
  2.  
  3. print \\  
  4. "hello world!" 

第2行~第3行代码是一个整体,调用print输出“hello world!”,这种情况不适合分行书写。)

类型系统

比如 int,string 等,type() 可以返回数据的类型,如:

>>> type(1)

<type 'int'>

>>> type('123')

<type 'str'>

python 尽管在声明变量的时候不指定类型,但变量其实是有类型的,用 c++0x 的概念来表达的话,实际上 python 的变量好像都是 auto 的,类型自动根据赋值推导出来。所以这样:

name = 'ddh'

verb = ' is '

noun = ' good man'

sentence = name + verb + noun

没有问题,但:

name = 'cyberscorpio'

age = 32

sentence = name + age

就会引发异常,因为字符串和数字不能直接相加。

可以通过 int() 或者 str() 强转类型,如上一句改成: sentence = name + str(age) 就不会有问题了。

容器类型

python 提供好用的两个容器:list 和 dict。插句题外话,其实最好用的容器还是 PHP 提供的关联数组,一个数组就包括了 python 中 list 和 dict 的全部功能,实在是很赞。

list

类似 array 的概念,例如:

lst = list()

lst.append('123')

lst.append('456')

lst.append(1000)

print lst

for x in lst:

    print x, ' type is ', type(x)

注意 list 内的数据可以是不同类型的,这一点会很方便。上面的输出是:

>>> print lst

['123', '456', 1000]  # 注意这里是 list 的字面表示方法,如 lst = ['123', '456', 1000],lst 自动成为一个 list

>>> for x in lst:

...     print x, ' type is ', type(x)

...

123  type is  <type 'str'>

456  type is  <type 'str'>

1000  type is  <type 'int'>  # 这个是整型的数据

dict

类似于 std::map 的概念,当然,和 list 类似,dict 的 key 和 value 不要求是同一种类型。如:

dct = dict()

dct['name'] = 'cyberscorpio'

dct['age'] = 32

dct['sex'] = 'male'

print dct

for k in dct:

print k, ' is ', dct[k]

输出为:

>>> print dct

{'age': 32, 'name': 'cyberscorpio', 'sex': 'male'}  # 注意这里是 dict 的字面表示方法,如 dct = {'age':32, 'name':'cyberscorpio', 'sex':'male'},则 dct 自动成为一个 dict

>>> for k in dct:

...     print k, ' is ', dct[k]

...

age  is  32

name  is  cyberscorpio

sex  is  male

判断一个 key 是否在 dict 中:

dct = {'name' : 'ddh', 'age' : 32, }

if 'name' in dct:

print 'the dict has a name and we will delete it...'

del dct['name']

可以使用 del 删除这个 key。另外 if key not in dict 可以判断这个 key 是否 “不在” dict 中。

list 和 dict 均为某种对象,它们支持的方法 (method) 可以通过语言内置的 dir() 来获取,比如:

>>> dir(lst)

['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__delslice__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getslice__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__setslice__', '__sizeof__', '__str__', '__subclasshook__', 'append', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort']

比如 append(),insert(),sort() 什么的,都是 list 很常用的方法。而:

>>> dir(dct)

['__class__', '__cmp__', '__contains__', '__delattr__', '__delitem__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__init__', '__iter__', '__le__', '__len__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'clear', 'copy', 'fromkeys', 'get', 'has_key', 'items', 'iteritems', 'iterkeys', 'itervalues', 'keys', 'pop', 'popitem', 'setdefault', 'update', 'values']

has_key(),keys(),values() 等都是 dict 常用的方法。

用 dir('123') 则可以看到 string 支持的所有方法,总之还是比较方便的。

循环

简单的 for 循环,如:

fox x in 容器:

对 x 做操作

这是对容器进行枚举的很方便的做法。

其他的循环方式还有 while,如:

while <测试>:

循环体

注意在循环中我们仍然有 break, continue 等跳出或者继续循环的指令,和 C/C++ 是一致的。

函数

函数定义:

def 函数名 (参数列表):

函数内容

值得注意的是,函数中变量默认是 local 的,如果要访问全局变量,那么需要在函数中声明其为 global,如:

bar = 0

def foo ():

   global bar

   bar = bar + 1

  print str(bar)

参数列表这里面颇有玄机,python 具备和 C++ 相同的两种参数列表结构,就是 (var1, var2) 和 (var1, var2=default value) 这两种,但除此之外呢,和 C/C++ 的 … 类似,python 还有两种针对不定参数个数的独门武功,举例说明:

def foo (*params):

   if len(params) == 0:

       print 'no parameter' # 如果调用方式为 foo() 则进入这里

  else:

       print params # 如果调用方式为 foo(1, 2, 3, 4),则这里输出为 (1, 2, 3, 4),params 是一个 tuple

def bar (**params):

   if len(params) == 0:

      print 'no parameter' # bar()

   else:

      print params # bar(name='ddh', age=32),则这里输出为 {'name' : 'ddh', 'age' : 32},params 是一个 dict。

对于 ** 这种方式,调用函数的时候,必须指定参数的名称和值,名称不需要带引号,但进入函数以后,自然成为同名的字符串,如上可见。

Python 所有的变量(不仅仅是传参)都是基于引用的,但是 python 的对象分为两种,一种叫 immutable,还有一种叫 mutable,区别在哪里呢?后者具有方法来改变自己,比如 list.append() 等,而 str 就是 immutable 的,也即,所有的 str 方法都不能改变自己(比如 str.replace() 是返回一个被替换过的 str,原来的 str 不变)。这样的话,我们这么来看:

def foo (s):

 s = '456'

name = '123'

foo(name)

首先,name 本身不是对象,它是对一个 str 且内容为 '123' 的 str 对象的引用。其次,在 foo 函数中, s 是对同一个对象的,引用,而不是对 name 的引用。那么可以断定:

  1. 对 s 的改变无法影响到 name,因为 name 并未被引用进来。
  2. s  = '456'  只是把 s 换成了对另一个内容为 '456' 的 str 对象的引用,无法改变之前那个 '123' 的 str 对象。
  3. 因为 str 不提供改变自己的方法,所以,foo 函数无法改变 name 所指向的对象的值。

这么来看的话,immutable 对象创建出来之后就不能变了,比如:

s = '123'

s = '456'

这里第一句,s 指向一个内容为 '123' 的 str 对象,第二句,s 指向一个内容为 '456' 的 str 对象。第一句中的对象,没有被任何人引用了,之后会被 “废料收集” 程序废掉,但我们不能改变它的内容。另:参见附录一 《对 python 中引用问题的再思考》。

除此之外,函数就没什么神奇的地方了,比如可以通过 return 返回一个值等等,这些都是老僧禅痰了。

TRY...EXCEPT

对于出现 traceback 的错误来讲,可以通过 try...except 来挽救。比如:

def safeint(sval):

try:

return int(sval)

except:

return -1

这样,safeint('xxxx') 就不会出现 traceback 了,因为 int('xxx') 会被 except 捕获,正常返回 -1。

第二节. 面向对象的 python

其实前面的 str, list 还有 dict 都是某种意义上的类 (class),只不过是内置的而已。如前所述,dir() 可以列出类和对象的方法 (method)。

class 和 self

和 c++ 一样,python 也用 class 定义一个类:

class 类名称:

"类的文档字串,用来简单说明这个类"

成员变量

def __int__ (self, 参数):  # [可选] 类似构造函数,但 __init__ 被(自动)调用时,对象已然被创建出来了

函数体

def __del__ (self):  # [可选] 类似析构

函数体

def 成员函数 (self, 参数):

函数体

习惯上,和 c++ 不同的是,python 一般使用 self (是一种习惯而非强制,在 def 的时候可以使用其他的名字比如 this)而不是 this,但两者功能上类似,值得注意的是,self 需要在方法的参数中写定,并且引用成员变量的时候,一定要通过 self。比如:

class Person:

"store information of a person"

name = ''

age = 0

sex = 'male'

def textOut(self):  # 这个 self 不可少

print 'name is: ', self.name, ' age is ', self.age, ' and sex is ', self.sex  # 对成员变量的引用,亦必须通过 self

@staticmethod

def sayHello ():

print 'hello,world!'

p = Person()

p.textOut()

p.sayHello()

Person.sayHello()

如果成员函数不带 self 参数,则类似 c++ 中静态函数的概念,不能访问成员变量,但是需要在函数前面加 @staticmethod  修饰 (独占一行),静态方法可以通过对象调用,亦可以通过类名调用。如上。

另外需要注意的是很多以两个下划线开头和结尾的函数(专用方法),这些往往都有特殊用途,比如上面提到的 __init__ 和 __del__,还有 __add__, __eq__, __ge__, __gt__, __lt__, __le__, __ne__, __getitem__, __str__ 等等。比如 __getitem__ 用来实现 [] 操作,而 __str__ 用来实现 str() 操作,等等。

继承

python 通过这样的语法形式来实现继承:

class Derived (Base1[, Base2[, …]]]):

类实现

可以有多个逗号分割开的基类,用以实现多继承。有两个细节要注意:

如果定义了 __init__ 函数,那么必须在 __init__ 中调用基类的 __init__,方法为 基类名.__init__(self[, 其他参数])

调用基类的函数,务必手工添加 self 参数

一个例子:

class Namable:

    name = ''

    def __init__ (self, name):

        self.name = name

    def output (self):

        print 'name is:', self.name

class Agable:

    age = 0

    def __init__ (self, age):

        self.age = age

    def output (self):

        print 'age is:', self.age

class Person (Namable, Agable):

    sex = 'male'

    def __init__ (self, name, age, sex = 'male'):

        Namable.__init__(self, name)  # 显式调用基类的 __init__,带 self 参数

        Agable.__init__(self, age)

        self.sex = sex

    def output (self):

        Namable.output(self)  # 调用基类的函数,亦需要带上 self 参数

        Agable.output(self)

        print 'sex is:', self.sex

    def do_output (self):

        self.output()  # 非基类函数,则无需手工加上 self 参数

p = Person('doudehou', 32)

p.do_output()

附录

附录一.  关于 python 中引用问题的再思考

python 中所有的变量都是引用,这一点需要时刻牢记在心。什么意思呢?举个例子, d1 = dict() ,这句话的意思就是,我们首先创建了一个类型为 dict 的 (匿名)对象,接着创建了一个变量 d1,然后让 d1 “引用” 这个 dict 的对象,如果类比于 C 语言的话,可以理解为 dict *d1 = new dict()。d1 它只是一个引用。

这里和 C 语言存在一个巨大的差异。这个差异是什么呢?我们知道  C 语言提供了解引用的操作符 (*),这样程序员可以通过 *d1 来获得这个对象的本体,而作为脚本语言的 python,却并没有(也没有必要)提供这个功能。因此,除了通过 “引用” (换句话说,就是指针)的方式之外,我们无从触摸到这个对象的本体。

想象一下,如果 C 语言中没有了对指针做 * 的运算,我们要如何来修改某个对象呢?比如:

char c = 'A';

char *p = &c;

我们手上只有一个 p,要怎么修改 *p 的内容呢?毫无疑问,我们没有办法。而这就正是 python 里的现实情况。char 就是属于 immutable 的类型,我们再举一个 C++ 的例子:

class foo {

char m_c;

public:

foo () : m_c('A') {}

void set (char c) {

m_c = c;

}

char get () {

return m_c;

}

};

foo *p = new foo();

std::cout << "foo: " << p->get() << std::endl;

p->set('B');

std::cout << "foo: " << p->get() << std::endl;

在上面这个例子里,如果说我们仍然没有对指针的 * 运算,但我们依然可以改变 *p 的内容,这是因为 p 所指向的对象,具有修改自身的能力(提供了相关的方法)。这种类型,在 python 里面,就是 mutable 的。我们常见的 list,dict 等等,就属于此类。而 python 的字符串类,则属于 immutable 的。

这里有两个地方需要注意:

  1. 变量赋值,实际上并没有产生对象的拷贝,仅仅是 “引用” (指针) 的拷贝。比如:

d1 = {'1' : 1, '2' : 2, }

d2 = d1

d2['3'] = 3

for k in d1:

print k, ' => ', d1[k]

上面的代码,我们会发现,d1['3'] 也存在了,而且值就是 3。所以我们可以把 python 里面的所有变量都想象成指针;所有的变量对变量的 = 操作都想象成指针的赋值,所有的 . 操作都想象成 ->,那么则庶几不会有问题了。

  1. 通过显式构造函数的方法来拷贝对象,还是上面的例子,如果我们用:

d1 = {'1' : 1, '2' : 2, }

d2 = dict(d1)

d2['3'] = 3

for k in d1:

print k, ' => ', d1[k]

这样,d2 和 d1 就没有一毛钱的关系了,这种情况下,dict(d1) 产生了一个新的(匿名)dict 对象,同时将 d2 变成对它的引用。如果我们把所有 x = Type(var) 的赋值想象成 x = new Type(var) 就相当于抓住了事物的本质。

原文地址:https://www.cnblogs.com/youxin/p/3059746.html