風炫安全web安全學習第三十七節課 15種上傳漏洞講解(二)
Python面向物件
@
目錄類
類(Class)是面向物件程式設計(OOP,Object-Oriented Programming)實現資訊封裝的基礎。類是一種使用者定義的引用資料型別,也稱類型別。每個類包含資料說明和一組操作資料或傳遞訊息的函式。類的例項稱為物件。百度百科的解釋
Python對於類的實現:
class MyClass: def method1(): print("method1") def method2(): print("method2") #==========分割(上面是類)======== myObj = Myclass()
其中MyClass就是類
物件
物件就是類的例項
上面myObj就是一個物件,它把類Myclass例項化了
類和物件的關係
類相當於是一種構想,一種想象,就很虛無縹緲,Python看到它以後不會給它分配記憶體空間,而物件就是這種想象的現實體。它們是設計圖與實體的關係。
類的方法及其呼叫
上面的程式碼中def method1()
和def method2()
就是Myclass類的兩個方法,可以通過例項化物件來呼叫
class MyClass: def method1(): print("method1") def method2(): print("method2") myObj = Myclass() myObj.method1() myObj.method2()
與Java類似Python也有構造方法預設的構造方法是init()
,構造方法是每次例項化一個物件的時候都會執行的一個方法,我們可以通過重寫它來滿足我們的一些要求。例如:
class Myclass:
def __init__(self):
print('正在呼叫init方法')
這樣每次例項化Myclass的時候就會執行print語句。
self
self相當於Java和C++裡面的this指標,它就相當於是一個引數,指代當前物件,這個引數的值是在你用這個方法的時候根據物件來傳入的。
比如上面的例子
class Myclass: def __init__(self): print('正在呼叫init方法') myObj = Myclass()
在執行myObj = Myclass()
的時候括號裡面雖然沒有引數,但是我們能理解成Python自動幫我們把myObj這個引數放入括號傳入類裡面,self就是指的這個引數,因為類在執行程式碼的時候需要知道它在操作那塊記憶體,self就是告訴他他要操作哪一塊記憶體。
許可權
python裡面幾乎沒有許可權控制,它有一個虛假的許可權控制手段就是把變數前面加兩個下劃線__
來完成變數私有化,這個被私有化的變數不能直接從外部訪問,只能從內部讀取。
class Myclass:
__mysecret = 10
def getmysecret(self):
return self.__mysecret
myObj = Myclass()
try:
myObj.__mysecret
except AttributeError as reason:
print("myObj.__mysecret出錯了" + str(reason))
print(myObj.getmysecret())
#只不過是把私有變數改了個名字而已
print(myObj._Myclass__mysecret)
Python的私有變數換了個名字,比如把"__name"就變成了"_類名__name"。
以上程式碼輸出如下
myObj.__mysecret出錯了'Myclass' object has no attribute '__mysecret'
10
10
面向物件三大基本特徵
封裝
封裝就是隱藏物件的屬性和實現細節,僅對外公開介面,控制在程式中屬性的讀和修改的訪問級別,將抽象得到的資料和行為(或功能)相結合,形成一個有機的整體,也就是將資料與操作資料的原始碼進行有機的結合,形成“類”,其中資料和函式都是類的成員。
舉個例子
class Mysum:
num1 = 2
num2 = 1
num3 = 3
def mysum(self,x, y):
return (x + self.num3) + (y - self.num1 - self.num2)
getSum = Mysum()
ans = getSum.mysum(1, 2)
print(ans)
這個輸出肯定是3,但是為什麼要用這麼愚蠢的演算法呢,這不歸使用者操心,我只需要把這個介面給你,告訴你它是求和的,具體是怎麼求的,對你是透明的,你不需要知道實現細節,只需要知道傳入兩個引數,它會返回它們的和就行,這就是封裝。
多型
多型同一個行為具有多個不同表現形式或形態的能力。是指一個類例項(物件)的相同方法在不同情形有不同表現形式。多型機制使具有不同內部結構的物件可以共享相同的外部介面。這意味著,雖然針對不同物件的具體操作不同,但通過一個公共的類,它們(那些操作)可以通過相同的方式予以呼叫。
例如
class cal:
def calu(slef, x):
return 0-x
def calu(self, x, y):
return x + y
test = cal()
print(test.calu(1))
print(test.calu(1, 2))
你以為這個就是python的多型嗎,不,錯了,這段程式碼執行會報錯,關於多型,Python和Java有天壤之別。
python不支援多型,也不用支援多型,python是一種多型語言,崇尚鴨子型別,一個物件有效的語義,不是有繼承自特定的類或實現特定的介面,而是由當前方法和屬性的集合決定。
下面就再解釋一下鴨子型別,鴨子型別中關注的不是物件型別本身,而是它是如何使用的,那麼python是一門多型語言但是不支援多型就是通過支援鴨子型別來說通的。
舉個Java的例子,Java實現多型:
public class test{
public void fun(int x){
System.out.println("輸入的引數只有一個");
}
public void fun(int x, int y){
System.out.println("輸入的引數有兩個")
}
public static void main(String[], args){
test t = new test();
t.fun(1);
t.fun(1, 2);
}
}
/*
=================輸出如下==================
輸入的引數只有一個
輸入的引數有兩個
*/
但是python裡面這樣寫是要報錯的!
但是Python沒必要這樣寫,它有鴨子型別,它可以這樣寫:
class A:
def fun(self):
print("A")
class B:
def fun(self):
print("B")
class C(A):
pass
class D(A):
def fun(self):
print("D")
class E():
pass
def getFun(arg):
try:
arg.fun()
except AttributeError as reason:
print("出錯了" + str(reason))
a = A()
b = B()
c = C()
d = D()
e = E()
getFun(a)
getFun(b)
getFun(c)
getFun(d)
getFun(e)
getFun('abc')
以下是輸出結果
A
B
A
D
出錯了'E' object has no attribute 'fun'
出錯了'E' object has no attribute 'fun'
很巧妙的小鴨子,注意這個getFun
函式,這種定義方法在不支援鴨子型別的語言裡面是絕對不允許的,你給它傳入一個引數arg,你卻不指定這個引數arg的型別,這好嗎,這不好,這容易邏輯混淆,這很好,這也恰恰凸顯了python的靈活。
這個函式它不管你傳入的是個什麼東西,我就把你傳進來的引數拿著去找你的fun方法,然後執行它。
如果你是一個物件而且正好有一個fun方法,那我執行那個fun方法。(例如A、B)
什麼?你不是一個物件?(python萬物皆物件,這裡說的可能不是很準確)那我就要報錯了。(例如最後一個)
什麼?你說你沒有這個方法?那我報錯。(例如倒數第二個)
什麼?你說你沒有這個方法但是你爹有?那我不管你爹的就是你的,我執行你爹的。(例如C)
什麼?你說你和你爹都有?那簡單,你的方法把你爹的覆蓋了,執行你的就行。(例如D)
這就是Python的多型。
繼承
定義
繼承可以使得子類具有父類的屬性和方法或者重新定義、追加屬性和方法等。(百度百科複製過來的定義)
格式
如果A繼承B,A是B的子類,則
class A:
def fun(self):
print("A_fun")
class B(A):
pass
b = B()
b.fun
#=======================輸出===========
A_fun
就是這樣B直接使用A的方法和屬性。
重寫
class A:
def fun(self):
print("A_fun")
class B(A):
def fun(self):
print("B_fun")
b = B()
b.fun
#=======================輸出===========
B_fun
就是如果子類中有方法名和父類一樣的的時候,會覆蓋掉父類的改方法,這就是重寫。
但是不影響父類本身方法。
多繼承
Python支援多繼承,什麼是多繼承,就是繼承很多個唄,比如
class A:
def fun_A(self):
print("fun_A")
class B:
def fun_B(self):
print("fun_B")
class C:
def fun_C(self):
print("fun_C")
class D:
def fun_A(self):
print("fun_D")
class E(A, B, C, D):
pass
e = E()
e.fun_A()
e.fun_B()
e.fun_C()
#===========輸出===============
fun_A
fun_B
fun_C
這就是多繼承,相當好理解。
需要注意的是,當父類方法名發生衝突時,繼承括號裡面排在前面的覆蓋後面的。
super關鍵字
super關鍵字的用法在Python2和Python3上面有一些不同,這裡以Python3為準來描述。
先說一下super在單繼承中的用法與意義
提個問題,如果我想在子類的fun方法中呼叫父類的fun方法怎麼辦
可以這樣寫
class A:
def fun(self):
print("A_fun")
class B(A):
def fun(self):
A.fun(self)
print("B_fun")
b = B()
b.fun()
#===========輸出結果================
A_fun
B_fun
還可以這樣寫
class A:
def fun(self):
print("A_fun")
class B(A):
def fun(self):
super().fun()
print("B_fun")
b = B()
b.fun()
那麼用super的好處在哪裡呢,暫時看不出來,但是當B現在不繼承A了,它要繼承一個C第二段程式碼只需要改成class B(C)
而第一段程式碼還要把A.fun(self)
改成C.fun(self)
,第二段程式碼可維護性就高了一點。
super在多繼承裡面可能會稍微複雜那麼一丟丟,但是多繼承我們能不用就不用,用多了程式碼越來越混。
組合
當幾個類沒有明顯的繼承關係但是又有一定聯絡的時候就可以用到組合技術
如果繼承是縱向深入,組合就是橫向擴充套件。
class count1:
def __init__(self, con1):
self.con1 = con1
class count2:
def __init__(self, con2):
self.con2 = con2
class sum3:
def __init__(self, x, y):
self.x = count1(x)
self.y = count2(y)
self.z = self.x.con1+self.y.con2
def pr(self):
print(self.z)
sum3_a = sum3(2, 3)
sum3_a.pr()
#=======輸出==========
5
這就是一個簡單的組合__init__
方法前面提到過,是一個構造方法。
python關於面向物件的幾個bif
名稱 | 引數型別 | 返回值型別 | 作用 |
---|---|---|---|
issubclass(B, A) | B和A都是類 | bool型別 | 判斷B是否是A的子類 |
isinstance(x1, A) | x1是例項化物件,A是類 | bool型別 | 判斷x1是否是A的例項化物件 |
hasattr(A, s) | A是類,s是字串 | bool型別 | 判斷A裡面是否有s屬性 |
getattr(x, s1, d) | x是物件,s1是字串,d可有可無,無所謂 | 屬性s1或d | 得到x的s1屬性,如果沒有就返回d |
setattr(x, s, d) | A是物件,s是字串,d是屬性s | void | 給x新增一個s屬性,並且初始值為d |
例子
class A:
pass
class B(A):
pass
print(issubclass(B, A))
x1 = A()
x2 = B()
print(isinstance(x1, A))
print(isinstance(x2, A))
print(isinstance(x1, B))
class D:
def __init__(self, x = 0):
self.x = x
d = D()
print(hasattr(d, 'x'))
print(getattr(d, 'x', "您所訪問的屬性不存在"))
print(getattr(d, 'y', "您所訪問的屬性不存在"))
setattr(d, 'y', 1)
print(getattr(d, 'y', "您所訪問的屬性不存在"))
#==========執行結果==============
True
True
True
False
True
0
您所訪問的屬性不存在
1
還有一個property,這個描述比較困難就不列表了
class pro:
def __init__(self, va = 10):
self.va = va
def setname(self, va):
self.va = va
def getname(self):
return self.va
def delname(self):
del self.va
pass
x = property(getname, setname, delname)
prop = pro()
print(prop.x)
prop.x = 20
print(prop.x)
del prop.x
try:
print(prop.x)
except AttributeError as reason:
print("出錯啦"+str(reason))
#==========執行結果==============
10
20
出錯啦'pro' object has no attribute 'va'
這個就是,怎麼說呢很方便吧,就是這種格式,操作起來就很方便,而且只需要向外面提過x這一個介面,內部可以大改不用改變很多介面。