## 引言:編程范式
編程是程序員用特定的語法+數據結構+算法組成的代碼來告訴計算機如何執行任務的過程 , 一個程序是程序員為了得到一個任務結果而編寫的一組指令的集合,正所謂條條大路通羅馬,實現一個任務的方式有很多種不同的方式, 對這些不同的編程方式的特點進行歸納總結得出來的編程方式類別,即為編程范式。 不同的編程范式本質上代表對各種類型的任務采取的不同的解決問題的思路, 大多數語言只支持一種編程范式,當然也有些語言可以同時支持多種編程范式。 兩種最重要的編程范式分別是面向過程編程和面向對象編程,當然還有人們常說的函數式編程。
**面向過程編程(Procedural Programming)**
面向過程編程依賴 , 面向過程又被稱為top-down languages, 就是程序從上到下一步步執行,一步步從上到下,從頭到尾的解決問題 。基本設計思路就是程序一開始是要著手解決一個大的問題,然后把一個大問題分解成很多個小問題或子過程,這些子過程再執行的過程再繼續分解直到小問題足夠簡單到可以在一個小步驟范圍內解決。
#### 面向對象編程(**Object-Oriented Programming )**
OOP編程是利用“類”和“對象”來創建各種模型來實現對真實世界的描述,使用面向對象編程的原因一方面是因為它可以使程序的維護和擴展變得更簡單,并且可以大大提高程序開發效率 ,另外,基于面向對象的程序可以使它人更加容易理解你的代碼邏輯,從而使團隊開發變得更從容。
## 面向對象編程幾大核心
**Class 類**
一個類即是對一類擁有相同屬性的對象的抽象、藍圖、原型。在類中定義了這些對象的都具備的屬性(variables(data))、共同的方法
**Object 對象?**
一個對象即是一個類的實例化后實例,一個類必須經過實例化后方可在程序中調用,一個類可以實例化多個對象,每個對象亦可以有不同的屬性,就像人類是指所有人,每個人是指具體的對象,人與人之前有共性,亦有不同
**Encapsulation 封裝**
在類中對數據的賦值、內部調用對外部用戶是透明的,這使類變成了一個膠囊或容器,里面包含著類的數據和方法
**Inheritance 繼承**
一個類可以派生出子類,在這個父類里定義的屬性、方法自動被子類繼承
**Polymorphism 多態**
多態是面向對象的重要特性,簡單點說:“一個接口,多種實現”,指一個基類中派生出了不同的子類,且每個子類在繼承了同樣的方法名的同時又對父類的方法做了不同的實現,這就是同一種事物表現出的多種形態。
## ?面向對象基礎介紹
好了,話不多說,我們先來看一下最簡單的定義一個類和使用的方法。
~~~
class Person(object): #定義一個類, class是定義類的語法,Person是類名,(object)是新式類的寫法。
def __init__(self,name,age,cn='中國'): #構造函數,為你實例化類的對象傳參數,類名加括號就會自動執行該方法。
self.name = name
self.age = age
def talk(self): #定義類的方法,為什么它們都要自動傳入一個self?下面我再解釋。。。
print('%s is talking'%self.name)
def __del__(self): #析構函數,在實例釋放、銷毀的時候自動執行,通常用于做一些收尾工作,如關閉一些數據庫連接,關閉打開的臨時文件等等,一般可以不寫。
print('結束了')
p1 = Person('wusir',22)
p1.talk()
~~~
那至于為什么會自動傳入一個self呢,我們看下面這個圖:

?根據上圖我們得知,**其實self,就是實例本身!你實例化時python會自動把這個實例本身通過self參數傳進去。**
## 面向對象的三大特性
### 封裝
封裝最好理解了。封裝是面向對象的特征之一,是對象和類概念的主要特性。
封裝,也就是把客觀事物封裝成抽象的類,并且類可以把自己的數據和方法只讓可信的類或者對象操作,對不可信的進行信息隱藏。
### 繼承
面向對象編程 (OOP) 語言的一個主要功能就是“繼承”。繼承是指這樣一種能力:它可以使用現有類的所有功能,并在無需重新編寫原來的類的情況下對這些功能進行繼承和擴展。
通過繼承創建的新類稱為“子類”或“派生類”。被繼承的類稱為“基類”、“父類”或“超類”。
繼承的過程,就是從一般到特殊的過程。
Python2 經典類是按深度優先來繼承的,新式類是按廣度優先來繼承的,
Python3 經典類和新式類都是統一按廣度優先來繼承的。
OK,那什么是經典類、新式類,什么又是深度優先和廣度優先。以后我會慢慢介紹。
### 多態
多態性(polymorphisn)是允許你將父對象設置成為和一個或更多的他的子對象相等的技術,賦值之后,父對象就可以根據當前賦值給它的子對象的特性以不同的方式運作。簡單的說,就是一句話:允許將子類類型的指針賦值給父類類型的指針。那么,多態的作用是什么呢?我們知道,封裝可以隱藏實現細節,使得代碼模塊化;繼承可以擴展已存在的代碼模塊(類);它們的目的都是為了——代碼重用。而多態則是為了實現另一個目的——接口重用!多態的作用,就是為了類在繼承和派生的時候,保證使用“家譜”中任一類的實例的某一屬性時的正確調用。好吧,我知道你沒看懂,簡單來說,多態就是:一種接口,多種實現。
```
Person(object):
def __init__(self, name, job):
self.name = name
self.job = job
def whoAmI(self):
print('I am a %s, my name is %s' % (self.job,self.name))
class Student(Person):
def __init__(self,name,job, gender):
super(Student, self).__init__(name,job)
self.gender = gender
class Teacher(Person):
def __init__(self,name,job,gender):
super(Teacher, self).__init__(name,job)
self.gender = gender
def who_am_i(x):
x.whoAmI()
s = Student('Bob', 'student', '男')
t = Teacher('Alice', 'teacher', '女')
who_am_i(s)
who_am_i(t)
點開瞅一眼
```
這個代碼很好的說明了這三大特性,代碼的封裝,繼承和多態。將功能封裝到類里面,同時Student類和Teacher類又繼承了Person類的屬性與方法,最后調用同樣的接口who\_am\_i,來輸出不同的結果。
```
~~~
I am a student, my name is Bob
I am a teacher, my name is Alice
~~~
```
## 經典類vs新式類
先試著用python2和python3分別把下面代碼執行一邊
```
class A:
def __init__(self):
self.n = 'A'
class B(A):
def __init__(self):
self.n = 'B'
class C(A):
def __init__(self):
self.n = 'C'
class D(B,C):
def __init__(self):
self.n = 'D'
obj = D()
print(obj.n)
```
然后分別用python2和python3按照順序,分別把D、B、C的構造函數注釋掉換成pass執行,你會發現,在python2中,幾次輸出分別是D、B、A、C,在python3中幾次輸出分別是D、B、C、A。但如果我們在A后面加上一個(object),那么它就從經典類變成了一個新式類,然后你再次在python2中像剛才那樣運行。你會發現這次的輸出結果由上次的D、B、A、C變成了D、B、C、A。python3再次運行無變化。
由此我們可以得出結果:
python2:
* 經典類:深度優先
* 新式類:廣度優先
python3
* 經典類:廣度優先
* 新式類:廣度優先
## 靜態方法
@staticmethod裝飾器即可把其裝飾的方法變為一個靜態方法,什么是靜態方法呢?其實不難理解,普通的方法,可以在實例化后直接調用,并且在方法里可以通過self.調用實例變量或類變量,但靜態方法是不可以訪問實例變量或類變量的,一個不能訪問實例變量和類變量的方法,其實相當于跟類本身已經沒什么關系了,它與類唯一的關聯就是需要通過類名來調用這個方法。
~~~
class Person(object):
def __init__(self,name):
self.name = name
@staticmethod #把eat方法變為靜態方法
def eat(self):
print("%s is eating" % self.name)
p = Person("jack")
p.eat()
~~~
上面的調用會出錯誤,說是eat需要一個self參數,但調用時卻沒有傳遞,沒錯,當eat變成靜態方法后,再通過實例調用時就不會自動把實例本身當作一個參數傳給self了。
解決方法有兩種:
* ?調用時主動傳遞實例本身給eat方法,即d.eat(d)?
* ?在eat方法中去掉self參數,但這也意味著,在eat中不能通過self.調用實例中的其它變量了
## 類方法
類方法通過@classmethod裝飾器實現,類方法和普通方法的區別是,類方法只能訪問類變量,不能訪問實例變量。并且類方法傳入的第一個參數為cls,是類本身。類方法可以通過類直接調用,或通過實例直接調用。但無論哪種調用方式,最左側傳入的參數一定是類本身。
~~~
class Dog(object):
name = 'jack'
def __init__(self, name):
self.name = name
@classmethod
def eat(cls):
print("%s is eating" % cls.name)
Dog.eat()
d = Dog("tom")
d.eat()
~~~
但無論上哪一種的調用方式,結果打印的都是一樣的:
~~~
jack is eating
jack is eating
~~~
## 屬性方法
屬性方法的作用就是通過@property把一個方法變成一個靜態屬性
~~~
class Dog(object):
def __init__(self,name):
self.name = name
@property
def eat(self):
print(" %s is eating" %self.name)
d = Dog("Jack")
d.eat()
~~~
上面這種代碼的運行會報錯這種錯誤,TypeError: 'NoneType' object is not callable,因為此時的eat已經是一個靜態屬性了,不能再通過加()來調用。
正常調用如下:
~~~
d = Dog("jack")
d.eat
輸出
jack is eating
~~~
PS:如果屬性方法還傳入了固定參數,想對這個固定參數進行修改怎么辦呢?因為我們知道,屬性方法的調用是不能加括號的,既然不能加括號,那就不能傳參數,擦,這樣的話,參數不就寫死了。。。當然是可以改的,可以在屬性方法的下方再寫一個同名的函數,用下面這種裝飾器??@函數名.setter? 來進行裝飾,可以在這個函數中進行參數的修改,當然還有裝飾器? @函數名.deleter? ?來裝飾函數,對屬性進行刪除操作。
## 面向對象中類成員的特殊方法
**1、\_\_doc\_\_ 表示類的描述信息**
~~~
class Foo:
""" 這是一個神奇的類 """
def func(self):
pass
print(Foo.__doc__)
f = Foo()
print(f.__doc__)
輸出結果:
這是一個神奇的類
這是一個神奇的類
~~~
PS:用類或者實例都可以進行調用該方法。
**2、\_\_module\_\_、\_\_class\_\_**
* \_\_module\_\_ 表示當前操作的對象在那個模塊
* \_\_class\_\_ ? ? 表示當前操作的對象的類是什么
```
~~~
class C:
def __init__(self):
self.name = 'wusir'
~~~
```
```
~~~
from lib.aa import C
obj = C()
print (obj.__module__ ) # 輸出 lib.aa,即:輸出模塊
print (obj.__class__) # 輸出 lib.aa.C,即:輸出類
~~~
```
**3、\_\_init\_\_? ?構造函數**,通過類創建對象時自動執行,用于完成對類的初始化操作
**4、\_\_del\_\_? ?析構函數**,當對象在內存中被釋放時,自動觸發執行。此方法一般無須定義,因為Python是一門高級語言,程序員在使用時無需關心內存的分配和釋放,因為此工作都是交給Python解釋器來執行,所以,析構函數的調用是由解釋器在進行垃圾回收時自動觸發執行的。
**5、\_\_call\_\_?**對象后面加括號,觸發執行。即:對象()或者類()()會觸發執行。
```
class Foo:
def __init__(self):
pass
def __call__(self, *args, **kwargs):
print('我是call')
obj = Foo() # 執行 __init__
obj() # 執行 __call__
輸出結果:
我是call
```
**6、\_\_dict\_\_?**查看類或對象中的所有成員
```
class Province:
country = 'China'
def __init__(self, name, count):
self.name = name
self.count = count
def func(self, *args, **kwargs):
print('func')
# 獲取類的成員,即:靜態字段、方法、
print(Province.__dict__)
# 獲取對象obj1的成員
obj1 = Province('HeBei', 10000)
print(obj1.__dict__)
# 獲取對象obj2的成員
obj2 = Province('HeNan', 3888)
print(obj2.__dict__)
```
輸出結果:
```
~~~
{'__module__': '__main__', 'country': 'China', '__init__': <function Province.__init__ at 0x000001875B266598>, 'func': <function Province.func at 0x000001875B266620>, '__dict__': <attribute '__dict__' of 'Province' objects>, '__weakref__': <attribute '__weakref__' of 'Province' objects>, '__doc__': None}
{'name': 'HeBei', 'count': 10000}
{'name': 'HeNan', 'count': 3888}
~~~
```
**7、 \_\_str\_\_?**如果一個類中定義了該方法,那么在打印對象時,默認輸出該方法的返回值
```
class Foo:
def __str__(self):
return 'wusir'
obj = Foo()
print(obj)
# 輸出結果 wusir
```
**8、 \_\_getitem\_\_,\_\_setitem\_\_,\_\_delitem\_\_**
```
class Foo(object):
def __getitem__(self, key):
print('__getitem__', key)
def __setitem__(self, key, value):
print('__setitem__', key, value)
def __delitem__(self, key):
print('__delitem__', key)
obj = Foo()
result = obj['k1'] # 自動觸發執行 __getitem__
obj['k2'] = 'wusir' # 自動觸發執行 __setitem__
del obj['k1'] #自動觸發執行 __delitem__,按上述寫法并未真正刪除,如需刪除,需在
#__delitem__ 方法中寫刪除操作
輸出:
__getitem__ k1
__setitem__ k2 wusir
__delitem__ k1
```
**9、\_\_new\_\_? \\? \_\_metaclass\_\_**
~~~
class Foo(object):
def __init__(self,name):
self.name = name
f = Foo("wusir")
~~~
上述代碼中,f是通過 Foo 類實例化的對象,其實,不僅 f 是一個對象,Foo類本身也是一個對象,因為在python中,一切皆對象,那么如果按照一切皆對象的理論:f對象是通過執行Foo類的構造方法創建,那么Foo類對象應該也是通過執行某個類的構造方法創建。
~~~
print type(f) # 輸出:<class '__main__.Foo'> 表示,obj 對象由Foo類創建
print type(Foo) # 輸出:<type 'type'> 表示,Foo類對象由 type 類創建
~~~
由上述打印結果我們可以發現:f是Foo的一個實例,Foo是type的一個實例,即:Foo類對象 是通過type類的構造方法創建。
那么,創建類就可以有兩種方式:
一般方式:
~~~
class Foo():
def func(self):
pass
~~~
特殊方式:
~~~
def func(self):
pass
Foo = type('Foo',(object,),{"func":func})
#type第一個參數:類名
#type第二個參數:當前類的基類
#type第三個參數:類的成員
~~~
```
def func(self):
print("hello %s"%self.name)
def __init__(self,name,age):
self.name = name
self.age = age
Foo = type('Foo',(object,),{'func':func,'__init__':__init__})
f = Foo("wusir",22)
f.func()
特殊方式加上構造方法
```
所以,要牢記:**類是由type類實例化產生的!**
那么問題來了,類默認是由 type 類實例化產生,type類中如何實現的創建類?類又是如何創建對象?
答:類中有一個屬性?\_\_metaclass\_\_,其用來表示該類由誰來實例化創建,所以,我們可以為?\_\_metaclass\_\_ 設置一個type類的派生類,從而查看類創建的過程。

```
class MyType(type):
def __init__(self, what, bases=None, dict=None):
print("--MyType init---")
super(MyType, self).__init__(what, bases, dict)
def __call__(self, *args, **kwargs):
print("--MyType call---")
obj = self.__new__(self, *args, **kwargs)
self.__init__(obj, *args, **kwargs)
class Foo(object):
__metaclass__ = MyType
def __init__(self, name):
self.name = name
print("Foo ---init__")
def __new__(cls, *args, **kwargs):
print("Foo --new--")
return object.__new__(cls)
# 第一階段:解釋器從上到下執行代碼創建Foo類
# 第二階段:通過Foo類創建obj對象
obj = Foo("Alex")
自定義元類
```
**類的生成 調用 順序依次是 \_\_new\_\_ --> \_\_init\_\_ --> \_\_call\_\_**
## ?反射
通過字符串映射或修改程序運行時的狀態、屬性、方法,?內置函數分別為:getattr、hasattr、setattr、delattr? 獲取成員、檢查成員、設置成員、刪除成員。
```
class Foo(object):
def __init__(self):
self.name = 'wusir'
def func(self):
return 'jack'
obj = Foo()
#### 檢查是否含有成員 ####
print(hasattr(obj, 'name'))
print(hasattr(obj, 'func'))
#### 獲取成員 ####
print(getattr(obj, 'name'))
print(getattr(obj, 'func')())
#### 設置成員 ####
print(hasattr(obj,'age'))
setattr(obj, 'age', 18)
print(getattr(obj,'age'))
#### 刪除成員 ####
delattr(obj, 'name')
print(hasattr(obj,'name'))
```
輸出結果:
```
True
True
wusir
jack
False
18
False
```
- Python學習
- Python基礎
- Python初識
- 列表生成式,生成器,可迭代對象,迭代器詳解
- Python面向對象
- Python中的單例模式
- Python變量作用域、LEGB、閉包
- Python異常處理
- Python操作正則
- Python中的賦值與深淺拷貝
- Python自定義CLI三方庫
- Python并發編程
- Python之進程
- Python之線程
- Python之協程
- Python并發編程與IO模型
- Python網絡編程
- Python之socket網絡編程
- Django學習
- 反向解析
- Cookie和Session操作
- 文件上傳
- 緩存的配置和使用
- 信號
- FBV&&CBV&&中間件
- Django補充
- 用戶認證
- 分頁
- 自定義搜索組件
- Celery
- 搭建sentry平臺監控
- DRF學習
- drf概述
- Flask學習
- 項目拆分
- 三方模塊使用
- 爬蟲學習
- Http和Https區別
- 請求相關庫
- 解析相關庫
- 常見面試題
- 面試題
- 面試題解析
- 網絡原理
- 計算機網絡知識簡單介紹
- 詳解TCP三次握手、四次揮手及11種狀態
- 消息隊列和數據庫
- 消息隊列之RabbitMQ
- 數據庫之Redis
- 數據庫之初識MySQL
- 數據庫之MySQL進階
- 數據庫之MySQL補充
- 數據庫之Python操作MySQL
- Kafka常用命令
- Linux學習
- Linux基礎命令
- Git
- Git介紹
- Git基本配置及理論
- Git常用命令
- Docker
- Docker基本使用
- Docker常用命令
- Docker容器數據卷
- Dockerfile
- Docker網絡原理
- docker-compose
- Docker Swarm
- HTML
- CSS
- JS
- VUE