1.class and instance

#class Student(object): #所有class都繼承自object,也可省略
# instance(實例),就是依照類別(class)創建的實際對象
class Student:
    # __init__ 方法就像建構子一樣,可在建立實例時,給予屬性,self等於是instance本身
    def __init__(self, name, score):
        self.name = name
        self.score = score

    def print_score(self):
        print('{}: {}'.format(self.name, self.score))

john = Student('John', 87) #john就是一個instance
john.print_score()
# result ==> John: 87
class Student:
    pass
s = Student()
# 可自由的給instance綁定屬性,這是python動態語言的特性
s.name = 'John'
# 在class中定義的函數和一般函數只差在第一個參數永遠是實例變數self,建立instance時不需要傳入self,會自動傳遞
# 在上面那個範例,我們把屬性和方法封裝在class中,當我們使用print_score方法時我們不需要知道裡面的邏輯,這就是封裝

2.存取限制

# 以第一個範例為例
john= Student('John', 87)
john.score
>>> 87
# 我們可以自由存取score屬性
john.score = 10
john.score
>>> 10
# 如果要讓外部不能這麼輕易存取屬性,要把屬性變數前面加上__,如下
class Student:
    def __init__(self, name, score):
        self.__name = name
        self.__score = score

    def print_score(self):
        print('{}: {}'.format(self.name, self.score))
john= Student('John', 87)
john.score
>>> AttributeError.....

class Student:
    def __init__(self, name, score):
        self.__name = name
        self.__score = score
    def print_score(self):
        print('{}: {}'.format(self.name, self.score))
    def get_score(self):
        return self.__score
    def set_score(self, score):
        self.__score = score

# 但為什麼一定要多寫一個方法來修改內部屬性呢?之前直接更改屬性,不是更容易嗎?
# 因為用set方法才可以對傳入參數做檢查,避免無效的參數,例如set_score方法不能讓它傳入字串或是數字範圍要在0~100之間!
def set_score(self, score):
        if 0 <= score <= 100:
            self.__score = score
        else:
            raise ValueError('invalid score')
# 當然加了雙底線,也不是不能直接存取變數,只是python把__name變成_Student__name,python本身是沒辦法阻擋這件事的
# 但在python中,__name__這種不算是私有變數

3.繼承

# 這邊就來個可能很多人看過的範例吧...
class Animal(object):
    def run(self):
        print('Animal is running')

# Dog and Cat類別就可以直接繼承Animal,繼承最大的用處就是子類別可以使用富類別的所有方法,不需要再寫一遍!!
class Dog(Animal):
    pass
class Cat(Animal):
    pass
dog = Dog()
dog.run()
==> Animal is running
# 但讓每個繼承Animal的子類別,run方法都是一樣的效果好像怪怪的,以下就是做到override覆寫的概念
class Dog(Animal):
    def run(self):
        print('Dog is running')
class Cat(Animal):
    def run(self):
        print('Cat is running')

4.多型

# 介紹多型前,再講一些概念
# 前面寫的Animal class、Dog class都屬於一種資料結構,跟python自帶的list、str等結構是一樣的
a = Animal()
d = Dog()
# isinstance可以判斷該instance是否屬於某類別
>>>isinstance(a, Animal)
True
>>>isinstance(d, Dog)
True
# d這個instance不只是Dog類別,還是Animal類別,因為Dog是繼承自Animal
>>>isinstnace(d, Animal)
True
# 但a卻不屬於Dog類別
>>>isinstnace(a, Dog)
False

5.獲得物件的資訊

type(123)
==> <class 'int'>
type('str')
==> <class 'str'>
type(None)
==> <type(None) 'NoneType'>

type(123)==int
==> True

# 若要比較是哪一種函數
import types
def fn():
    pass

type(fn)==types.FunctionType
==> True
type(abs)==types.BuiltinFunctionType
==> True
type(lambda x: x)==types.LambdaType
==> True
type((x for x in range(10)))==types.GeneratorType
==> True

# 可以查看該物件的所有屬性、方法
dir('test')
==> ['__add__',...]
# 類似__xxx__的屬性或方法是有特殊用意的,像__len__方法等於使用len()方法,會回傳物件長度
len('test')
==> 4
'test'.__len__()
==> 4

# 可透過這三個方法getattr()、setattr()以及hasattr()來操作物件的屬性和方法
class MyObject:
    def __init__(self):
        self.x = 9
obj = MyObject()
hasattr(obj, 'x') # 判斷是否有x屬性
==> True
setattr(obj, 'y', 19) # 增加一個屬性y
hasattr(obj, 'y') 
==> True
getattr(obj, 'y') # 取得屬性y,沒有這個屬性會報Attribute Error
==> 19
getattr(obj, 'z', 404) # 如果取不到該屬性,可以給一個預設值

sum = obj.x + obj.y # 能用這種方法取值就不要用下面的方法!!!
sum = getattr(obj, 'x') + getattr(obj, 'y')

# 只有不確定該物件是否有該屬性時才會使用,類似下面的用法
def getData(func):
    if hasattr(func, 'read'):
        return 'good'
    return None

6.實例屬性和類別屬性

# 給實例綁定屬性有兩種方法
# (1)在定義class時,用self來定義變數
class Student:
    def __init__(self, name):
        self.name = name

s = Student('a')
# (2)直接綁定屬性給實例變數
s.score = 90

# 但如果是Student類別本身需要屬性時,這種就是類別屬性
# 當定義類別屬性時,雖然是屬於類別的屬性,但該類別的所有instance都可以存取該類別屬性!!!!
class Student:
    name = 'Student'
# 舉例來說
s = Student()
print(s.name) # 因為實例沒有該屬性,所以會去類別找
==> Student
print(Student.name) 
==> Student

# 有個小問題,可以更加了解實例屬性和類別屬性!!
# 給Student類別設置一個屬性count,當增加一個學生實例時,會統計學生人數,count會自動加1

# 先看一個錯誤範例
# 在python裡,所有東西都是物件!
# 像整數0也是一個物件,所以當我們寫count=0,等於把count這個屬性指向0這個物件
# self.count += 1,但是int類別是immutable的,所以python只能把self.count指向新的物件1,而不是修改原本指向的物件0
class Student(object):
    count = 0

    def __init__(self, name):
        self.name = name
        self.count += 1 # 錯誤,這個是指向實例的count

class Student(object):
    count = 0

    def __init__(self, name):
        self.name = name
        Student.count += 1 # 正確

class Student(object):
    count = []

    def __init__(self, name):
        self.name = name
        self.count.append(1)

results matching ""

    No results matching ""