杂记5:Python官网的The Python Tutorial笔记
博客专区 > fzyz_sb 的博客 > 博客详情
杂记5:Python官网的The Python Tutorial笔记
fzyz_sb 发表于3年前
杂记5:Python官网的The Python Tutorial笔记
  • 发表于 3年前
  • 阅读 235
  • 收藏 0
  • 点赞 0
  • 评论 0

腾讯云 技术升级10大核心产品年终让利>>>   

摘要: 学习资料来自: https://docs.python.org/2/tutorial/index.html

1. Python中列表赋值的问题

    在看官网时候,我个人写了如下的代码练习切割操作:

In[68]: letters
Out[68]: ['a', 'b', 'C', 'D', 'E', 'f', 'g']
In[69]: letters[2:5] = ['c', 'd', 'e']
In[70]: letters
Out[70]: ['a', 'b', 'c', 'd', 'e', 'f', 'g']
In[71]: letters[2:5] = ('C', 'D', 'E')
In[72]: letters
Out[72]: ['a', 'b', 'C', 'D', 'E', 'f', 'g']
    我当时的第一个疑问是:
In[71]: letters[2:5] = ('C', 'D', 'E')
In[72]: letters
Out[72]: ['a', 'b', 'C', 'D', 'E', 'f', 'g']
这里letters为什么不会被修改为:['a', 'b', ('C', 'D', 'E'), 'f', 'g' ].

    然后想明白了对于切割操作,外层的[]是语法糖,代表的里面包含的元素要替换letters中的列表,这时('C', 'D', 'E')实际上被解释为['C', 'D', 'E'],则上述的赋值操作,可以解释如下:

letters = ['a', 'b'] + ['c', 'd', 'e'] + ['f', 'g']
letters[2:5] = ('C', 'D', 'E') ==> ['a', 'b'] + ['C', 'D', 'E'] + ['f', 'g'] ==> ['a', 'b', 'c', 'd', 'e', 'f', 'g']
如果我们非要赋值元祖,可以提供一个列表,里面的数据是元祖即可:
In[74]: letters[2:5] = [('c', 'd', 'e')]
In[75]: letters
Out[75]: ['a', 'b', ('c', 'd', 'e'), 'f', 'g']
    这也解释了为什么我们清空列表可以这样编写:
In[76]: letters[:] = []
In[77]: letters
Out[77]: []


2. for循环的基本使用

    C++的STL支持迭代器,而Python中也直接类似的操作.例如我们遍历一个列表,通常有以下两种方法:

# -*- coding:utf-8 -*-
nums = range(0, 10)
#类似迭代器
for num in nums:
    print num,
print
#使用下标索引
for i in range(0, len(nums)):
    print nums[i],
print
    如果我们想要修改列表,通过索引则可以很简单实现:
#使用索引进行列表的修改
for i in range(0, len(nums)):
    nums[i] += 10
#[10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
print nums
    而实际上通过迭代器无法实现,我们可以测试如下:
nums = [1, 2]
#[140520361897032, 140520361897008]
print [id(num) for num in nums]
#使用迭代实现修改元素
#140520361896792 140520361896768
for num in nums:
    num += 10
    print id(num),
print
#[1, 2]
print nums

     for循环中的每个num为数值,是不可改变的,所以每次执行+操作,会新建一个num,然后存储新值.这也是为什么这里的id会完全不同的原因.

    而且使用迭代器操作改变原列表有一个很隐藏的BUG!:

# -*- coding:utf-8 -*-
words = ['a', 'bb', 'ccc']
for w in words:
    print w
    if len(w) > 2:
        words.insert(0, w)

print words
这段代码会死循环直到内存溢出~_~

所以一般我们会修改成:

# -*- coding:utf-8 -*-
words = ['a', 'bb', 'ccc']
for w in words[:]:
    if len(w) > 2:
        words.insert(0, w)
#['ccc', 'a', 'bb', 'ccc']
print words

    for循环还有一个非常好用的属性:判断for循环是否完整运行:

# -*- coding:utf-8 -*-
nums = range(0, 3)
#类似迭代器
for num in nums:
    print num,
else:
    print '完整运行一遍'

for num in nums:
    if num > 1:
        break
else:
    print '这句话不会输出'
    
#输出如下: 0 1 2 完整运行一遍

    在循环中还有两个函数特别有用:enumerate和zip:

In[52]: for i, v in enumerate(['a', 'b', 'c']):
...     print i, v
...     
0 a
1 b
2 c
而zip可以将多个列表进行操作:
In[53]: for key, value in zip([0, 1, 2], ['a', 'b', 'c']):
...     print key, '=>', value
...     
0 => a
1 => b
2 => c


3. 函数的基本使用

    函数传参的基本原则是:传递一个对象,而非副本:

# -*- coding:utf-8 -*-
nums = [1, 2]
def func(nums):
    nums += [11, 12]

func(nums)
#[1, 2, 11, 12]
print nums
    函数名也只是一个对象,所以存在赋值操作:
# -*- coding:utf-8 -*-
def func():
    print 'hello world'

f = func
#hello world
f()
#4408107568
print id(f)
#4408107568
print id(func)
    函数传参时要遵循以下原则:

保证所传递的参数不被函数所修改,被修改的变量当做函数返回值,而非函数的参数.

例如:

# -*- coding:utf-8 -*-
def func(words, nums = []):
    newStr = words + ",".join(nums)
    return newStr

nums = ['1', '2']
newStr = func("hello", nums)
#['1', '2']
print nums
#hello1,2
print newStr
    我之前曾经思考过所传递的参数必须为元祖,例如传递参数时候将列表转换为元祖,在函数内部将元祖再转换为列表等,但是对于字典则很难办,而且代码会非常的繁琐.所以就按照上面的原则进行函数的编写.

    缺省参数的写法可以让我们完成类似C++的重载操作:

# -*- coding:utf-8 -*-
def func(words, nums = []):
    print words
    if nums:
        print nums

#hello
func('hello')
#hello
#[1, 2, 3]
func('hello', [1, 2, 3])


4. 列表的基本使用

1. 基本函数

list.append(x): 向后插入一个元素,等价于:a[len(a):] = [x]

list.extend(L): 通过插入列表L来达到扩展的目的,等价于a[len(a):] = L

list.insert(index, x): 在当前索引index前插入元素x

list.remove(x): 移除元素值等于x的元素.如果此元素不存在,则报错.

list.pop([i]): 移除指定索引的元素并返回删除元素的值.如果不带参数索引,则默认删除最后一个元素并返回它.

list.index(x): 返回值等于x的元素的索引,如果不存在则报错.

list.count(x): 返回x在列表中出现的次数

list.sort(cmp = None, key = None, reverse = False): 排序列表

list.reverse(): 反转列表

# -*- coding:utf-8 -*-
nums = [1, 2]
#[1, 2, 3]
nums.append(3)
#[1, 2, 3, 4, 5]
nums.extend([4, 5])
#[1, 1.11, 2, 3, 4, 5]
nums.insert(1, 1.11)
#[1, 2, 3, 4, 5]
nums.remove(1.11)
#3
print nums.pop(2)
#5
print nums.pop()
#2
print nums.index(4)
#1
print nums.count(1)
nums.reverse()
#[4, 2, 1]
print nums
2. 对列表有用的三个内建函数

filter(function, sequence): 对序列sequence中每个元素item执行function(item),返回为True组合的序列.除了str, unicode和tuple,其余的都返回列表

# -*- coding:utf-8 -*-
#[1, 3, 5, 7, 9]
print filter(lambda x: x % 2, range(0, 10))
#defg
print filter(lambda x: x > 'c', 'abcdefg')
#(4, 5)
print filter(lambda x: x > 3, tuple(range(0, 6)))
map(function, sequence): 与filter类似,但是function需要几个参数,则就需要几个序列
# -*- coding:utf-8 -*-
#[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
print map(lambda x: x * x, range(0, 10))
#[0, 2, 6, 12, 20, 30, 42, 56, 72, 90]
print map(lambda x, y: x * y, range(0, 10), range(1, 11))
reduce(function, sequence): 读取序列的前两个元素进行function,然后生成的结果作为第一个元素,和第三个元素继续组成两个元素,然后继续function.如果提供初始值,则以初始值作为第一个元素:
# -*- coding:utf-8 -*-
def add(x, y):
    return x + y
#55
print reduce(add, range(1, 11))

def mul(x, y):
    return x * y
#240
print reduce(mul, range(1, 5), 10)


5. 推导式的基本使用

    最常用的是列表推导式:

In[35]: [(x + 1) for x in range(0, 10)]
Out[35]: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
    但是如果一个序列很大,例如我们通过推导式来读取一个文件,则我们需要生成器表达式(个人理解为元祖推导式)来完成:
In[38]: ss = ((x + 1) for x in range(0, 1000))
In[39]: ss
Out[39]: <generator object <genexpr> at 0x108764cd0>
In[40]: for i in ss:
...     if i > 10:
...         break
...     print i,
...     
1 2 3 4 5 6 7 8 9 10
    假设是集合set,那么使用推导式则需要花括号:
In[42]: a = {x for x in 'abracadabra' if x not in 'abc'}
In[43]: a
Out[43]: {'d', 'r'}
In[44]: set(['d', 'r'])
Out[44]: {'d', 'r'}


6. 模块化

    在大型项目中,模块化是非常重要的.但是我们导入模块时候有两种方式,假设我们定义了一个模块文件:

__author__ = 'lgt'

def fib(n):
    a, b = 0, 1
    while b < n:
        print b,
        a, b = b, a + b

def fib2(n):
    result = []
    a, b = 0, 1
    while b < n:
        result.append(b)
        a, b = b, a + b
    return result
一般情况下我们会这样导入:
In[15]: import fibo
In[16]: fibo.fib(100)
1 1 2 3 5 8 13 21 34 55 89
In[17]: fibo.fib2(100)
Out[17]: [1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]
    但是有时候为了书写方便,我们往往会这样导入:
In[18]: from fibo import fib, fib2
In[19]: from fibo import *
In[20]: fib(100)
1 1 2 3 5 8 13 21 34 55 89
In[21]: fib2(100)
Out[21]: [1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]
    但是这会隐藏一个BUG.例如我们在另一个文件fibo1.py中定义了相同的fib函数:
__author__ = 'lgt'

def fib(n):
    print 'hello world'
那么那个模块最后导入,就使用那个模块:
In[22]: from fibo import *
In[23]: from fibo1 import *
In[24]: fib(100)
hello world
In[25]: from fibo1 import *
In[26]: from fibo import *
In[27]: fib(100)
1 1 2 3 5 8 13 21 34 55 89
    所以,具体取舍还是得靠程序员自己.

__name__ == '__main__'的妙用

    一般情况下,我们是需要对一个模块进行单元测试的编写的.这时我们往往可以在模块中添加类似如下指令:

if __name__ == '__main__':
    import sys
    fib(int(sys.argv[1]))
因为只有运行当前模块情况下,__name__才会等于'__main__',而如果其它模块调用它,则__name__等于'fibo'.


7. 序列化json

    序列化用代码就可以说明:

# -*- coding:utf-8 -*-
import json
f = open('test.txt', 'w')
json.dump([1, 'simple', 'list'], f)
f.close()

with open('test.txt', 'r') as f:
    for line in f:
        print line
        print type(line)

with open('test.txt', 'r') as f:
    x  = json.load(f)
    print x
    print type(x)
输出:
[1, "simple", "list"]
<type 'str'>
[1, u'simple', u'list']
<type 'list'>


8. 异常处理

    异常通常使用try ... catch 来捕获.而else表示没有产生异常时必须执行的语句,finally表示无论是否发生异常,都会被执行的语句:

# -*- coding:utf-8 -*-
import sys

try:
    a = 1 / 1
except (RuntimeError, TypeError, NameError) as e:
    print 'handler error', e
else:
    print 'no error'
finally:
    print 'finish'

#输出:
#no error
#finish
而产生异常的代码:
# -*- coding:utf-8 -*-
import sys

try:
    a = 1 / 0
except (RuntimeError, TypeError, NameError, ZeroDivisionError) as e:
    print 'handler error', e
else:
    print 'no error'
finally:
    print 'finish'

#输出:
#finish


9. Classes

1. Python的作用域和命名空间

    命名空间就是一堆对象的名字集合,而对象通常具有不同的生命周期.而作用域就是命名空间可以直接访问的文本域,且作用域通常是动态生成的(Python是解释性语言).

例如,我们定义一个函数func,则产生一个作用域:

# -*- coding:utf-8 -*-

def func():
    x = 1
    del x
    这是,执行x = 1实际上就是将变量x绑定到函数func所产生的作用域中,而del x就是接触绑定.

2. 类对象

    类对象支持两种操作: 属性引用和实例化.假设我们定义一个类:

class MyClass:
    """A simple example class"""
    i = 12345
    def __init__(self, i):
        self.i = i
    def f(self):
        return 'hello world'

属性引用:

print MyClass.i    #12345
MyClass.i = 10000
print MyClass.i    #10000
    而我们一般都使用实例化:
x = MyClass(12345)
print x.f()     #hello world
print x.i       #12345
3. 实例化变量

    类变量针对类而言,而实例化变量针对具体对象而言.假设我们编写如下代码:

# -*- coding:utf-8 -*-

class Dog:
    kind = 'canine'

    def __init__(self, name):
        self.name = name

d = Dog('Fido')
e = Dog('Buddy')
print d.kind    #canine
print e.kind    #canine
print d.name    #Fido
print e.name    #Buddy
    这里类变量和实例变量互不影响.但假设我们这样编写代码:
# -*- coding:utf-8 -*-

class Dog:
    tricks = []

    def __init__(self, name):
        self.name = name
    def add_trick(self, trick):
        self.tricks.append(trick)

d = Dog('Fido')
e = Dog('Buddy')
d.add_trick('roll over')
e.add_trick('play dead')
print d.tricks  #['roll over', 'play dead']
    这不是新需求,是BUG......

所以我们一般都是这样设计:

# -*- coding:utf-8 -*-

class Dog:
    def __init__(self, name):
        self.name = name
        self.tricks = []
    def add_trick(self, trick):
        self.tricks.append(trick)

d = Dog('Fido')
e = Dog('Buddy')
d.add_trick('roll over')
e.add_trick('play dead')
print d.tricks  #['roll over']

4. 继承

    Python中的继承机制如C++的虚拟继承,即所有的函数都可以被重写,可直接被调用,或者根本不调用:


# -*- coding:utf-8 -*-

class BaseClass(object):
    def __init__(self, name):
        self.baseName = name
    def show(self):
        print self.baseName
    def getName(self):
        return self.baseName
    def baseFunc(self):
        print "I'm baseClass"

class SubClass(BaseClass):
    def __init__(self, name, num):
        super(SubClass, self).__init__(name)
        self.subNum = num
    def show(self):
        super(SubClass, self).show()
        print self.subNum
    def getName(self):
        return self.baseName + str(self.subNum)

C = SubClass('lcj', 26)
#继承父函数
C.show()
#调用自身函数
print C.getName()
#调用父函数
C.baseFunc()
输出:



lcj
26
lcj26
I'm baseClass
    而Python中私有变量往往以__(双下划线开头),这在设计类的时候很有用:



# -*- coding:utf-8 -*-

class Mapping:
    def __init__(self, iterable):
        self.items_list = []
        #私有变量
        self.__update(iterable)
    def update(self, iterable):
        for item in iterable:
            self.items_list.append(item)

    __update = update

class MappingSubclass(Mapping):
    def update(self, keys, values):
        for item in zip(keys, values):
            self.items_list.append(item)

S = MappingSubclass([11, 12])
S.update(['a', 'b'], [1, 2])
#[11, 12, ('a', 1), ('b', 2)]
print S.items_list





共有 人打赏支持
粉丝 362
博文 209
码字总数 447144
×
fzyz_sb
如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
* 金额(元)
¥1 ¥5 ¥10 ¥20 其他金额
打赏人
留言
* 支付类型
微信扫码支付
打赏金额:
已支付成功
打赏金额: