#!/usr/bin/env python3
# -*- coding: utf-8 -*-
r'''
類: 類和實例,訪問控制,繼承、多態
'''
__author__ = "Kingrumn"
import types
# 定義類
# 通過class關鍵字定義類
# class後面緊接着是類名,即Student,類名通常是大寫開頭的單詞
# (object),表示該類是從哪個類繼承下來的, 如果沒有合適的類繼承,用object總是沒錯的
class Student1(object):
pass
# 創建實例
# 通過類名+()創建實例
stu = Student1()
print(stu) # <__main__.Student object at 0x0145AEB0>
class Student2(object):
# 通過定義一個特殊的__init__方法,在創建實例的時候,就把name,score等屬性綁上去
def __init__(self, name, age):
# 第一個參數永遠是self,表示創建的實例本身
self.name = name
self.age = age
# 和普通的函數相比,在類中定義的函數只有一點不同,就是第一個參數永遠是實例變量self,
# 調用時,不用傳遞該參數
# 除此之外,類的方法和普通函數沒有什麼區別
def print_age(self):
print(self.age)
# 給類增加新的方法
def get_grade(self):
if self.age > 18:
return "大學"
else:
return "中學"
# 數據封裝
# 同樣的,可以在類外部通過封裝定義print_age函數
def print_age(s):
print(s.age)
# 增加__init__方法後,在創建實例的時候,就不能傳入空的參數了,必須傳入與__init__方法匹配的參數,但self不需要傳
tom = Student2("Tom", 15)
print(tom.age, tom.name) # 15 Tom
tom.print_age() # 15
print_age(tom) # 15
jack = Student2("Jack", 19)
print(tom.name, tom.get_grade()) # Tom 中學
print(jack.name, jack.get_grade()) # Jack 大學
# 訪問限制
# 通過添加__使屬性變爲私有屬性,外部不可訪問
class Student3(object):
# 屬性的名稱前加上兩個下劃線__, 則變爲私有屬性
def __init__(self, name, age):
self.__name = name
self.__age = age
# 屬性私有後,需要提供訪問接口
def get_age(self):
return self.__age
def set_age(self, age):
self.__age = age
def get_name(self):
return self.__name
def set_name(self, name):
self.__name = name
# 通過提供方法提供訪問私有屬性的能力
def get_grade(self):
if self.__age > 18:
return "大學"
else:
return "中學"
# 內部可以訪問,外部不可以訪問
jack = Student3("Jack", 19)
print(jack.get_grade()) # 大學
# print(jack.__name) # AttributeError: 'Student3' object has no attribute '__name'
jack.set_name("Jacky")
jack.set_age(15)
print(jack.get_name(), jack.get_age()) # Jacky 15
# 在Python中,變量名類似__xxx__的,也就是以雙下劃線開頭,並且以雙下劃線結尾的,是特殊變量,
# 特殊變量是可以直接訪問的,不是private變量,所以,不能用__name__、__score__這樣的變量名
# 有些時候,你會看到以一個下劃線開頭的實例變量名,比如_name,
# 這樣的實例變量外部是可以訪問的,
# 但是,按照約定俗成的規定,當你看到這樣的變量時,意思就是,“雖然我可以被訪問,但是,請把我視爲私有變量,不要隨意訪問”
# 一種錯誤的認知
jack.__name = "xxx"
print(jack.get_name(), jack.__name) # Jacky xxx
# 你會發現__name並非類的屬性,雖然賦值是成功的,但是這是完全不同的2個東西
# 繼承和多態
class Animal(object):
def run(self):
print('Animal is running...')
class Dog(Animal):
pass
class Cat(Animal):
# 也可以對子類增加一些方法,比如Dog類:
def run(self):
# 子類的run方法覆蓋了父類的run方法
print('Cat is running')
def eat(self):
print('Cat is Eating meat...')
# 上例中,Animal是Dog和Cat的父類、基類、超類;Dog和Cat是Animal的子類
# 子類獲得了父類的全部功能
dog = Dog()
dog.run() # Animal is running...
cat = Cat()
cat.run() # Cat is running
cat.eat() # Cat is Eating meat...
# 多態定義
def who_run(animal):
animal.run()
# dog和cat都是animal,所以都可以傳進來
dog.run() # Animal is running...
cat.run() # Cat is running
# 著名的“開閉”原則
# 對擴展開放:允許新增Animal子類;
# 對修改封閉:不需要修改依賴Animal類型的who_run()等函數
# 對於Python這樣的動態語言來說,則不一定需要傳入Animal類型。我們只需要保證傳入的對象有一個run()方法就可以了
# 對於靜態語言(例如Java)來說,如果需要傳入Animal類型,則傳入的對象必須是Animal類型或者它的子類,否則,將無法調用run()方法
class Wind(object):
def run(self):
print("wind is running...")
wind = Wind()
# 只要wind定義了run方法就可以,不需要一定是Animal類或者其子類
who_run(wind) # wind is running...
# 獲取對象信息
# 使用type()
print(type(wind)) # <class '__main__.Wind'>
# 基本數據類型,int/str/bool/float
# 類型常量:import types:types.FunctionType、types.BuiltinFunctionType、types.LambdaType、types.GeneratorType
print(type((x * x for x in range(10))) == types.GeneratorType) # True, 不推薦這樣使用,可使用isinstance()
# 使用isinstance(), 總是優先使用isinstance()判斷類型,可以將指定類型及其子類“一網打盡”。
print(isinstance(dog, Dog)) # True
print(isinstance(dog, Animal)) # True
# 使用dir()
# 獲得一個對象的所有屬性和方法, 它返回一個包含字符串的list
print(dir(tom))
# [
# '_Student3__age', '_Student3__name',
# '__class__', '__delattr__',
# '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__',
# '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__',
# '__le__', '__lt__', '__module__', '__name', '__ne__', '__new__', '__reduce__',
# '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__',
# '__weakref__',
# 'get_age', 'get_grade', 'get_name', 'set_age', 'set_name'
# ]
# 配合getattr()、setattr()以及hasattr(),我們可以直接操作一個對象的狀態
print(hasattr(tom, '__name')) # False
setattr(tom, 'score', 19)
print(getattr(tom, 'score')) # 19
# 實例屬性和類屬性
# 給實例綁定屬性的方法是通過實例變量,或者通過self變量:
jack.instance_attr = 58 # 實例屬性,其他實例是無法訪問的
jack.get_name() # 訪問的是類屬性,所有實例均可以訪問
更多更及時的博客更新請戳—> KingRumn