python學習——基礎(七)

在Python中,一個.py文件就稱之爲一個模塊(Module)。 java

使用模塊有什麼好處? python

最大的好處是大大提升了代碼的可維護性。其次,編寫代碼沒必要從零開始。當一個模塊編寫完畢,就能夠被其餘地方引用。咱們在編寫程序的時候,也常常引用其餘模塊,包括Python內置的模塊和來自第三方的模塊。 ssh

模塊別名: 函數

try:
    import cStringIO as StringIO
except ImportError: # 導入失敗會捕獲到ImportError
    import StringIO

面向對象的訪問限制: spa

在Class內部,能夠有屬性和方法,而外部代碼能夠經過直接調用實例變量的方法來操做數據,這樣,就隱藏了內部的複雜邏輯。 設計

若是要讓內部屬性不被外部訪問,能夠把屬性的名稱前加上兩個下劃線__,在Python中,實例的變量名若是以__開頭,就變成了一個私有變量(private),只有內部能夠訪問,外部不能訪問
調試

class Student(object):
    def __init__(self, name):
        self.__name = name

    def getName(self):
        return '實例變量:'+self.__name

stu1 = Student('syc')
print stu1.getName() # 實例變量:syc
print stu1.__name # AttributeError: type object 'Student' has no attribute '__name'


須要注意的是,在Python中,變量名相似__xxx__ 的,也就是以雙下劃線開頭,而且以雙下劃線結尾的,是特殊變量,特殊變量是能夠直接訪問的,不是private變量,因此,不能用__name__ __score__ 這樣的變量名。

有些時候,你會看到以一個下劃線開頭的實例變量名,好比_name,這樣的實例變量外部是能夠訪問的,可是,按照約定俗成的規定,當你看到這樣的變量時,意思就是,「雖然我能夠被訪問,可是,請把我視爲私有變量,不要隨意訪問」。 code

繼承: orm

在OOP程序設計中,當咱們定義一個class的時候,能夠從某個現有的class繼承,新的class稱爲子類(Subclass),而被繼承的class稱爲基類、父類或超類(Base class、Super class)。 對象

class Animal(object):
    def run(self):
        print 'animail is running'

    def eat(self):
        print 'animail is eating'

class Horse(Animal):

    def run(self):
        print 'horse is running'
        Animal.eat(self) # 子類中調用父類的方法

hs = Horse()
hs.run()
print type(hs)

結果:
horse is running
animail is eating
<class '__main__.Horse'>


若是要得到一個對象的全部屬性和方法,可使用dir()函數:

['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'eat', 'run']


僅僅把屬性和方法列出來是不夠的,配合getattr() setattr() 以及hasattr() ,咱們能夠直接操做一個對象的狀態

class Horse(object):
    name = 'horse'
    def run(self):
        print 'horse is running'
        Animal.eat(self) # 子類中調用父類的方法

hs = Horse()
print hasattr(hs, 'name') # 判斷hs對象是否含有name屬性,一樣也能夠進行方法的判斷
setattr(hs, 'age', '18') # 增長的是實例變量,不能爲對象設置新的方法
print getattr(hs, 'age') # 獲得hs的age值,同時能夠獲得方法的變量


使用__slots__

正常狀況下,當咱們定義了一個class,建立了一個class的實例後,咱們能夠給該實例綁定任何屬性和方法,這就是動態語言的靈活性

class Student(object):
    name = 'syc'

    def drink(self):
        print 'student is drunking'

stu = Student()
stu.age = 18 #給實例增長屬性
print stu.age
Student.age =19 # 給類添加屬性
print Student.age

def eat(self):
    print 'student is eating'

from types import MethodType
stu.eat = MethodType(eat, stu, Student) # 給Student的實例stu添加方法
stu.eat()

stu2 = Student()
# print stu2.eat() # AttributeError: 'Student' object has no attribute 'eat'

Student.eat = MethodType(eat, None, Student) # 給類Student添加方法
stu2.eat()


   可是,若是咱們想要限制class的屬性怎麼辦? 爲了達到限制的目的,Python容許在定義class的時候,定義一個特殊的__slots__ 變量,來限制該class能添加的屬性和方法
class Student(object):
    __slots__ = ('name', 'age', 'eat')
    name = 'syc'

    def drink(self):
        print 'student is drunking'
stu = Student()
stu.address = 'fuzhou'

結果:AttributeError: 'Student' object has no attribute 'address'

方法也是一樣的;

使用__slots__要注意,__slots__定義的屬性僅對當前類起做用,對繼承的子類是不起做用的

class Student(object):
    __slots__ = ('name', 'age', 'eat')
    name = 'syc'

    def drink(self):
        print 'student is drunking'

class SubStudent(Student):
    pass

substu = SubStudent()
substu.address = 'fuzhou'
print substu.address  # fuzhou


使用@property

@property普遍應用在類的定義中,可讓調用者寫出簡短的代碼,同時保證對參數進行必要的檢查,這樣,程序運行時就減小了出錯的可能性。

class Student(object):

    @property
    def aname(self):  # 第一處
        return self.__name

    @aname.setter # 第二處
    def name(self, name): # 第三處
        # if name == 'syc':
        #     raise TypeError('name不能爲syc')
        self.__name = name


stu = Student()
stu.name = 'syc' # 第四處
print stu.name # 第五處


第一處和第二處的name必須相同,第三處、第四處、第五處必須相同

新增的屬性爲name,不是__name

多重繼承:

Mixin:在設計類的繼承關係時,一般,主線都是單一繼承下來的,若是須要「混入」額外的功能,經過多重繼承就能夠實現,這種設計一般稱之爲Mixin;python中的多重繼承相似於java中的類組合;

在多重繼承中,最大的問題就是當繼承的父類中有同名的方法,這時的優先級問題:

class Grandfa(object):
    def hair(self):
        print 'no hair'

class Father(Grandfa):
    pass

class Mom(object):
    def hair(self):
        print 'hair'

class Tom(Father,Mom):
    pass

tom = Tom()
tom.hair()  # no hair 深度優先


從例子能夠看出,聽從的是深度優先的準則;


定製類:

經過特殊變量,能夠爲咱們定製類,如:__slots__,__len__,__str__等

__str__:

class Student(object):
    pass
    # def __str__(self):
    #     return 'a student instance'

stu = Student()
print stu # <__main__.Student object at 0x02634B90>

class Student(object):
    # pass
    def __str__(self):
        return 'a student instance'

stu = Student()
print stu # a student instance


還有一個相似的__repr__; 二者的區別是__str__() 返回用戶看到的字符串,而__repr__() 返回程序開發者看到的字符串,也就是說,__repr__() 是爲調試服務的。


__iter__:

判斷對象是否能夠進行迭代:

class Student(object):
    pass

from collections import Iterable
print isinstance(stu,Iterable) # False


若是須要使對象可以迭代,能夠在類中定義__iter__方法:


class Student(object):

    def __iter__(self):
        return self

    def next(self):
        self.a, self.b = self.b, self.a + self.b
        if self.b >100:
            raise StopIteration()
        return self.b

stu = Student()

from collections import Iterable
print isinstance(stu,Iterable) # True
stu.a, stu.b = 1,2
print [x for x in stu] # [3, 5, 8, 13, 21, 34, 55, 89]


Python的for循環就會不斷調用該迭代對象的next()方法拿到循環的下一個值,直到遇到StopIteration錯誤時退出循環。


__getitem__

雖然能做用於for循環

class Student(object):

    def __getitem__(self, n):
        for x in range(n):
            self.a, self.b = self.b, self.a + self.b
        return self.b

stu = Student()

from collections import Iterable
print isinstance(stu,Iterable) # False
stu.a, stu.b = 1,2
print stu[1] # [3, 5, 8, 13, 21, 34, 55, 89]



,可是不能像list按照下標取出元素,須要實現__getitem__方法:

__getattr__

經過__getattr__動態的定義類的屬性和方法;

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

    def __getattr__(self, item):
        if item == 'score': # 自動添加屬性
            return 99

        if item =='getSocre': # 自動添加方法
            return lambda :self.score * self.score

stu = Student('syc')
print stu.socre # None ==>當類定義了__getattr__時,即便沒有定義socre,也不會報錯:AttributeError: 'Student' object has no attribute 'socre'
print stu.score # 99
print stu.getSocre() # 9801



class Student(object):

    def __init__(self, path=''):
        self.__path = path
    def __getattr__(self, item):
        if item == "users":
            return lambda user: Student("%s/users/:%s" % (self.__path, user))
        else:
            return Student("%s/%s" % (self.__path, item))

    def __str__(self):
        return self.__path


print Student().users('syc').repos # /users/:syc/repos



或者:

class Student(object):

    def __init__(self, path=''):
        self.__path = path
    def __getattr__(self, item):
        return Student("%s/%s" % (self.__path, item))

    def __str__(self):
        return self.__path

    def __call__(self, name):
        return Student("%s/:%s" % (self.__path, name))


print Student().users('syc').repos # /users/:syc/repos




__call__

一個對象實例能夠有本身的屬性和方法,

當咱們調用實例方法時,咱們用instance.method()來調用。能不能直接在實例自己上調用呢?相似instance()?在Python中,答案是確定的。

任何類,只須要定義一個__call__()方法,就能夠直接對實例進行調用。請看示例:

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

    def __call__(self):
        print('My name is %s.' % self.name)

s = Student('syc')
s() # My name is syc.



__call__() 還能夠定義參數。對實例進行直接調用就比如對一個函數進行調用同樣,因此你徹底能夠把對象當作函數,把函數當作對象,由於這二者之間原本就沒啥根本的區別。

那麼,怎麼判斷一個變量是對象仍是函數呢?其實,更多的時候,咱們須要判斷一個對象是否能被調用,能被調用的對象就是一個Callable對象,好比函數和咱們上面定義的帶有__call()__的類實例:

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

    def __call__(self):
        print('My name is %s.' % self.name)

s = Student('syc')
print callable(s) # True
print callable([1, 2, 3]) # False
print callable('ABC') # False
print callable(None) # False



經過callable() 函數,咱們就能夠判斷一個對象是不是「可調用」對象。

使用元類:

建立類有兩種方法:自定義:class XXX,type(ClassName, parentClass, dict{fun,property})

def fn(self, name):
    print 'Hello %s'% name
Hello = type('Hello', (object,), dict(hello=fn))
Hello().hello('world') # Hello world



要建立一個class對象,type()函數依次傳入3個參數:

  1. class的名稱;
  2. 繼承的父類集合,注意Python支持多重繼承,若是隻有一個父類,別忘了tuple的單元素寫法;
  3. class的方法名稱與函數綁定,這裏咱們把函數fn綁定到方法名hello上。
經過type() 函數建立的類和直接寫class是徹底同樣的,由於Python解釋器遇到class定義時,僅僅是掃描一下class定義的語法,而後調用type() 函數建立出class。
相關文章
相關標籤/搜索