python學習筆記(四)

1. map()函數

高階函數,它接收一個函數 f 和一個 list,並通過把函數 f 依次作用在 list 的每個元素上,得到一個新的 list 並返回,不改變原有列表。

def format_name(s):
    return s[0].upper()+s[1:].lower()
    
print map(format_name, ['adam', 'LISA', 'barT'])
輸出:['Adam', 'Lisa', 'Bart']

2. reduce()函數

高階函數,接收參數一個函數 f,一個list,f 必須接收兩個參數,reduce()對list的每個元素反覆調用函數f,並返回最終結果值。f還可以接收第三個參數,作爲初始計算值。

def prod(x, y):
    return x*y

print reduce(prod, [2, 4, 5, 7, 12])
計算過程:2*4 = 8,8*4 = 32....
輸出:3360

3. filter()函數

高階函數,接收一個函數 f 和一個list,函數 f 的作用是對每個元素進行判斷,返回 True或 False,filter()根據判斷結果自動過濾掉不符合條件的元素,返回由符合條件元素組成的新list。

import math
def is_sqr(x):
    return int(math.sqrt(x))**2==x
  
print filter(is_sqr, range(1, 101))
輸出:[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

4. sorted()函數

高階函數,對list進行排序,可以接收一個比較函數來實現自定義排序,比較函數的定義是,傳入兩個待比較的元素 x, y,如果 x 應該排在 y 的前面,返回 -1,如果 x 應該排在 y 的後面,返回 1。如果 x 和 y 相等,返回 0,默認從小到大排序。

#自定義排序函數
def cmp_ignore_case(s1, s2):
    if s1.upper()>s2.upper():
        return 1
    if s1.upper()<s2.upper():
        return -1
    else:
        return 0
print sorted(['bob', 'about', 'Zoo', 'Credit'],cmp_ignore_case) 
#這裏函數必須放在列表後面,不然會報函數對象不是迭代錯誤!

5. python中返回函數

def calc_sum(lst):
    def lazy_sum():
        return sum(lst)
    return lazy_sum
#這裏內層函數引用了外層函數的變量lst,稱之爲閉包。
>>> f = calc_sum([1, 2, 3, 4])
>>> f #f是個函數,參數已經確定
<function lazy_sum at 0x1037bfaa0>
>>> f() #調用f函數,輸出值
>>> 10

返回函數不要引用任何循環變量,或者後續會發生變化的變量。

def count():
    fs = []
    for i in range(1, 4):
        def f(j): #閉包函數g沒有直接引用循壞變量i,所以可以獲得3個參數值不一樣的函數。
            def g():
                return j*j
            return g
        fs.append(f(i))
    return fs

f1, f2, f3 = count()
print f1(), f2(), f3()
輸出:149

6. 匿名函數

lambda x: x * x 匿名函數只能有一個表達式,不寫return

#strip()用於去除字符串首尾的指定字符,默認空格或換行符
print filter(lambda s : s and len(s.strip())>0,['test', None, '', 'str', '  ', 'END'])

7. decorator 裝飾器

當要對原有函數進行修改是,可以通過高階函數返回新函數,並將其傳給原有函數名,從而覆蓋原有函數。可以減少重複性代碼。
Python中的@語法是爲了簡化裝飾器調用。
@log 打印日誌
@performance 檢測性能
@transaction 數據庫事物
@#post(’/register’) URL路由

Python的 *args 和 kw,保證任意個數的參數總是能正常調用
args表示任何多個非關鍵字參數,它是一個tuple;**kwargs表示關鍵字參數,它是一個dict。並且同時使用args和
kwargs時,必須*args參數列要在**kwargs前

import time
#不帶參數的decorator,f是要檢測的函數
def performance(f):
    def fn(*args,**kw):
        t1 = time.time() #函數執行前時間
        r = f(*args,**kw)
        t2 = time.time() #函數執行後時間
        print 'call %s() in %d' %(f.__name__,t2-t1) #f.__name__  是函數名
        return r
    return fn

@performance
def factorial(n):
    return reduce(lambda x,y: x*y, range(1, n+1))

print factorial(10)
輸出:
call factorial() in 0
3628800
############有參數的decorator########
import time

def performance(unit): #unit參數
    def log_decorator(f): #f函數作爲參數
    	@functools.wraps(f) #把原函數屬性複製到新屬性中,在返回的新函數前用
        def fn(*args,**kw):
            t1 = time.time()
            r = f(*args,**kw)
            t2 = time.time()
            print 'call %s() in %d%s' %(f.__name__,t2-t1,unit)
            return r
        return fn
    return log_decorator

@performance('ms')
def factorial(n):
    return reduce(lambda x,y: x*y, range(1, n+1))

print factorial(10)
輸出:
call factorial() in 0ms
3628800

decorator返回的新函數函數名已經不是’factorial’,而是@log內部定義的’fn’,這對於那些依賴函數名的代碼就會失效。所以需要把原函數的一些屬性複製到新函數中。用functools.wraps(f)

8、偏函數

functools.partial可以把一個參數多的函數變成一個參數少的新函數,少的參數需要在創建時指定默認值,這樣,新函數調用的難度就降低了。

>>> import functools
>>> int2 = functools.partial(int, base=2) #轉化爲二進制,這樣就不用每次都輸入base參數
>>> int2('1000000')
64
#cmp(a,b) 如果啊a<b 返回-1,a>b 返回1 ,否則 0
>>> sorted_ignore_case = functools.partial(sorted,cmp =  lambda s1,s2:cmp (s1.lower(),s2.lower())) #從小到大排序
>>> print sorted_ignore_case(['bob', 'about', 'Zoo', 'Credit'])
['about', 'bob', 'Credit', 'Zoo']

9. 動態模塊導入

導入模塊錯誤是執行其他導入

try:
    import json
except ImportError:
    import simplejson as json

10. 使用__future__

可以在Python舊版本中引入新版本的功能

#在Python 3.x中,字符串統一爲unicode,2.7則要加u
from __future__ import unicode_literals

s = 'am I an unicode?'
print isinstance(s, unicode)

11.類

按照 Python 的編程習慣,類名以大寫字母開頭,緊接着是(object),表示該類是從哪個類繼承下來的。
類的實例屬性可以像變量一樣操作。
__attr :兩個下劃線開頭的屬性是私有屬性,無法被外部訪問。

class Person(object):
    pass
#可以爲類實例分別定義不同屬性
p1 = Person()
p1.name = 'Bart' 
p2 = Person()
p2.grade = 2
#可以爲Person類添加一個特殊的__init__(self)方法,這樣類實例都會有方法裏定義的屬性
#**kw 定義關鍵字參數,參數個數可以不用固定
class Person(object):
    def __init__(self,name,gender,birth,**kw):
        self.name = name
        self.gender = gender
        self.birth = birth
        for k,v in kw.iteritems(): #遍歷kw關鍵字參數,self.k = v
            setattr(self,k,v)
xiaoming = Person('Xiao Ming', 'Male', '1990-1-1', job='Student',age = 19)

print xiaoming.name
print xiaoming.job

類屬性

當實例屬性和類屬性重名時,實例屬性優先級高

#__init__之前寫的屬性,可以不用先創建實例,再訪問,放入實例也可以訪問這個屬性
class Person(object):
    count = 0 #定義初始值
    def __init__(self,name):
        self.name = name
        Person.count += 1 #每創建一個實例都會調用一次__init__,count屬性就會+1

實例方法和類實例

實例的方法就是在類中定義的函數,它的第一個參數永遠是 self,調用實例方法必須在實例上調用,其他參數和一個普通函數是完全一樣。
在實例方法內部,可以訪問所有實例屬性,這樣,如果外部需要訪問私有屬性,可以通過方法調用獲得

class Person(object):

    def __init__(self, name, score):
        self.name = name
        self.score = score
        self.get_grade = lambda: 'A' #這裏的get_grade是函數,因爲不需要實例傳入參數

p1 = Person('Bob', 90)
print p1.get_grade
print p1.get_grade()

類方法
在class中定義的全部是實例方法,實例方法第一個參數 self 是實例本身。

class Person(object):

    __count = 0

    @classmethod #將方法綁定到Person類上
    def how_many(cls): #參數名爲cls,cls.__count 相當於 Person.count
        return cls.__count 
    def __init__(self,name):
        self.name = name
        Person.__count+=1

print Person.how_many() #直接訪問類方法

12. 類的繼承

Python從一個類繼承,可以是object,class MyClass (object): pass
調用super().__init__初始化父類

class Person(object):
    def __init__(self, name, gender):
        self.name = name
        self.gender = gender

class Teacher(Person):
#super(Teacher,self)返回Teacher繼承的父類Person,然後調用__init__方法初始化父類,不然沒有name,gender屬性,__init__中self已經在super中隱式傳入,不能寫
    def __init__(self, name, gender, course):
        super(Teacher,self).__init__(name,gender)
        self.course = course
p = Person('Tim', 'Male')
t = Teacher('Alice', 'Female', 'English')
isinstance(p,Teacher) 
False #不能認爲父類是子類,因爲子類比父類多了寫屬性和方法
isinstance(t,Person) 
True #子類可以認爲是父類

如果父類和子類有相同的方法,那麼方法調用將作用在實際類型上,這個行爲叫多態。

多重繼承
從多個父類繼承,稱爲多重繼承。
dir()返回的屬性是字符串列表,如果已知一個屬性名稱,要獲取或者設置對象的屬性,就需要用 getattr() 和 setattr( )函數了。
getattr(s, 'age', 20) 獲取s的age屬性值,沒有就返回20
setattr(s, 'name', 'Adam') 設置name屬性值,沒有就新增屬性

13.python 中的一些特殊方法

定義在類中,把類的實例變成str就需要用到__str__()特殊方法或__repr__()方法,其中__str__()是給用戶看,而__repr__()是給開發人員看。

 class Student(Person):

    def __init__(self, name, gender, score):
        super(Student, self).__init__(name, gender)
        self.score = score

    def __str__(self):#定義print實例時的輸出形式
        return '(Student:%s,%s,%s)' %(self.name,self.gender,self.score) 
    def __cmp__(self, s): #對類的實例進行排序需要用到__cmp__()
        if self.score == s.score:
            return cmp(self.name, s.name) #成績一樣的按名字排序,列表元素僅是類實例,否則會報錯
        return -cmp(self.score, s.score)

如果一個類表現得像一個list,要獲取有多少個元素,就得用__len()__ 函數。

#斐波那契數列
class Fib(object):

    def __init__(self, num):
        a,b = 0,1
        l = []
        for i in range(num):
            l.append(a)
            a,b = b,a+b
        self.numbers = l #初始化numbers屬性,並複製l
        
    def __str__(self): #把實例變成字符串格式,不然print f時不是列表形式,不過這也不影響計算實例元素個數!
        return str(self.numbers)
    def __len__(self):
        return len(self.numbers)

f = Fib(10)
print f
print len(f)
from __future__ import division
class Rational(object):
    def __init__(self, p, q):
        self.p = p
        self.q = q

    def __int__(self):
        return self.p // self.q

    def __float__(self):
        return self.p / self.q


print int(Rational(7, 2))
print float(Rational(1, 3))

@property

class Student(object):

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

    @property
    def score(self): #獲取成績,不需要參數
        return self.__score

    @score.setter #是@property 的副產品,設置成績,要有一個score參數
    def score(self,score):
        if score < 0 or score > 100:
            raise ValueError('invalid score')
        self.__score = score
        
    @property
    def grade(self): #相當於新增屬性grade
        if self.score >= 80:
            return 'A'
        elif self.score >= 60:
            return 'B'
        return 'C'

s = Student('Bob', 59)
print s.grade

s.score = 60
print s.grade

s.score = 99
print s.grade

Python是動態語言,任何實例在運行期都可以動態地添加屬性。如果要限制添加屬性的範圍,可以用__slots__來實現。

class Person(object):
	#限制屬性範圍,放在__init__之前
    __slots__ = ('name', 'gender')
    def __init__(self, name, gender):
        self.name = name
        self.gender = gender

所有的函數都是可調用對象,一個類實例也可以變成一個可調用對象,只需要實現一個特殊方法__call__()。

class Fib(object):

    def __call__(self,num):
        a,b,l = 0,1,[]
        for i in range(num):
            l.append(a)
            a, b = b, a + b
        return l
    

f = Fib()
print f(10) #調用實例對象
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章