1. 程式人生 > >Python--面向物件進階

Python--面向物件進階

isinstance和issubclass

isinstance

isinstance(obj1,obj2):判斷物件與類之間的關係,判斷第一個引數是否是第二個引數的例項。

>>> n1 = 10
>>> isinstance(n1, int) #判斷n1是否是數字型別,如果是返回True如果不是防護False
True

>>> class A(object):
...     pass
...
>>> a1 = A()
>>> isinstance(a1, A)  # 判斷a1是否是類A的物件,如果是返回True,如果不是返回False
True

type()函式和isinstance()函式兩者有什麼區別呢?

>>> print(type(1) is int)
True
>>> print(isinstance(1, int))
True

#從上面的結果看,兩者的結果都是True,那麼type()與isinstance()的區別在哪呢?
#從接下來的例子,就能夠清晰看出來。

class A:pass
class B(A):pass

b = B()

print(isinstance(b, A)) # True
print(isinstance(b, B)) #
True print(type(b)) # <class '__main__.B'> print(type(b) is B) # True print(type(b) is A) # False #總結: isinstance()是可以用在繼承的關係上;而type()不能用來檢測繼承關係。

issubclass

issubclass(obj1,obj2):用來描述一個類與另一個類之間的關係,判斷一個類是否是另一個類的子類

class A:pass
class B(A):pass

print(issubclass(B, A)) #
True print(issubclass(A, B)) # False # 總結: 類B是類A的子類,類A不是類B的子類

反射

什麼是反射?

反射的概念是由Smith在1982年首次提出的,主要是指程式可以訪問、檢測和修改它本身狀態或行為的一種能力(自省)。這一概念的提出很快引發了電腦科學領域關於應用反射性的研究。它首先被程式語言的設計領域所採用,並在Lisp和麵向物件方面取得了成績。

Python中的反射:通過字串的形式操作物件相關的屬性。python中的一切事物都是物件(都可以使用反射)

四個可以實現自省的函式:hasattr getattr setattr delattr

下列方法適用於類和物件(一切皆物件,類本身也是一個物件)

  hasattr   getattr   setattr   delattr

類使用反射

類:靜態屬性 類方法 靜態方法

class Student(object):
    name = "小白"

    @classmethod
    def check_course(cls):
        print("檢視課程")

    @staticmethod
    def login():
        print("登入")

# 檢查是否含有某屬性
print(hasattr(Student, "login"))    # 檢測Student類中是否有login方法,如果有返回True,沒有返回False

# 通過反射檢視靜態屬性
print(getattr(Student, 'name'))

# 通過反射呼叫方法
print(getattr(Student, "check_course"))  # 得到的是類方法的記憶體地址
getattr(Student, "check_course")()  # 呼叫類方法
print(getattr(Student, "login"))    # 得到的是靜態方法的記憶體地址
getattr(Student, "login")() # 呼叫靜態方法

# 一般情況hasattr和getattr聯用, 示例:
num = input('>>>')  # 等待使用者輸入
if hasattr(Student, num):   # 判斷使用者輸入的方法是否有,如果有才執行。
    getattr(Student, num)()

物件使用反射

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

    def func(self):
        print(" In func ")


a = A("小白")
print(a.name)
print(getattr(a, "name"))
getattr(a, "func")()

模組使用反射

import os
os.rename('__init__.py', 'init')
getattr(os, 'rename')('init', '__init__.py')    # os.rename('init', '__init__.py')

自定義模組使用反射

import sys
def wahaha():
    print("wahaha")

def qqxing():
    print("qqxing")

wahaha()
qqxing()

my_file = sys.modules['__main__']
getattr(my_file, 'wahaha')()
getattr(my_file, 'qqxing')()

反射總結

# 反射
# hasattr, getattr
# 類名.名字
    # getattr(類名, '名字')
# 物件名.名字
    # getattr(物件, '名字')
# 模組名.名字
    # import 模組
    # getattr(模組, '名字')
# 自己檔案.名字
    # import sys
    # getattr(sys.modules['__main__'], '名字')

反射的應用

#! /usr/bin/env python
# -*- coding: utf-8 -*-
# __author__ = "yanjieli"
# Date: 2018/11/25/025

import sys


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


class Manager(Int):
    OPERATE_LIST = [
        ('建立課程', 'create_course'),
        ('招聘老師', 'create_Teacher'),
        ('檢視課程', 'check_course'),
        ('檢視學生資訊', 'check_userinfo'),
    ]

    def create_course(self):
        print("建立課程")

    def create_Teacher(self):
        print("招聘老師")

    def check_course(self):
        print("檢視課程")

    def check_userinfo(self):
        print("檢視學生資訊")


class Teacher(Int):
    OPERATE_LIST = [
        ('檢視課程', 'check_course'),
        ('檢視學生成績', 'check_achievement'),
    ]

    def check_course(self):
        print("檢視課程")

    def check_achievement(self):
        print("檢視學生成績")


class Student(Int):
    OPERATE_LIST = [
        ('檢視課程', 'check_course'),
        ('選擇課程', 'choose_course'),
        ('檢視已選課程', 'choosed_course'),
        ('jsdklfjskld', 'aa'),
    ]

    def check_course(self):
        print("檢視課程")

    def choose_course(self):
        print("選擇課程")

    def choosed_course(self):
        print("檢視已選課程")


def login():
    username = input('user:>>>')
    password = input('password:>>>')
    with open("user_info", encoding="utf-8") as f:
        for line in f:
            user, pwd, ident = line.strip().split("|")
            if username == user and password == pwd:
                print("歡迎您!%s" % user)
                return user, ident
        else:
            print("登入失敗")
            exit()


def main():
    user, id = login()
    cls = getattr(sys.modules['__main__'], id)  # 通過反射拿到當前登入使用者所屬的類,如果是學生,則拿到學生類;
    operate_list = cls.OPERATE_LIST
    obj = cls(user)
    for index, i in enumerate(operate_list, 1):
        print(index, i[0])      # 打印出所有的功能
    option = int(input("option: >>>"))
    option_itme = operate_list[option -1]   # 拿到的是一個元組,比如:('建立課程', 'create_course'),
    getattr(obj, option_itme[1])()     # 通過反射拿到所輸入的選項對應的方法並執行


main()
反射應用示例

內建方法

內建方法:內建方法就是不需要程式設計師定義,本身就存在類中的方法。內建方法又稱為雙下方法,魔術方法。

內建方法通常長成這樣:__名字__。 每一個雙下方法都有它自己的特殊意義

所有的雙下方法,都不需要我們直接呼叫,都有另外一種自動觸發它的方法。而是總有一些其他的 內建方法 特殊的語法 來自動觸發這些 雙下方法

__call__

__call__ 方法的執行是由物件後加括號觸發的,即:物件()。擁有此方法的物件可以像函式一樣被呼叫。

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def __call__(self, *args, **kwargs):
        print('呼叫物件的__call__方法')


a = Person('張三', 24)  # 類Person可呼叫
a()  # 物件a可以呼叫
class A(object):
    def __call__(self, *args, **kwargs):
        print("執行了call方法")


class B(object):
    def __init__(self, cls):
        print("在例項化A之前做一些事情")
        self.obj = cls()
        self.obj()
        print("在例項化A之後做一些事情")


a = A()
a()     # 物件() == 相當於呼叫__call__方法

A()()   # 類名()() 相當於先例項化得到了一個物件,再對物件(), ==> 和上面的結果一樣,相當於呼叫__call__方法
B(A)

__len__

計算物件的長度,如果類中定義了__len__方法,那麼在物件中就能用內建函式len(obj),就會自動呼叫__len__方法。

class Foo(object):
    def __init__(self, s):
        self.s = s

    def __len__(self):
        print('呼叫了__len__方法, 計算長度')
        return len(self.s)


a = Foo("aaaa")
print(len(a))   # 自動呼叫了__len__方法
'''
列印結果:
呼叫了__len__方法, 計算長度
4
'''

__new__

網上經常有一個笑話“程式設計師可以自己new一個物件”, 到底new有什麼作用呢?

__new__又稱為構造方法,通過__init__()方法,我們知道初始化一個例項需要經歷的幾個步驟中的第一步就是在記憶體空間中開闢一個物件空間,這個物件空間是__init__方法開闢的麼?其實不是,在init之前,例項化物件的第一步是通過__new__方法建立一個物件空間。

class Fuu():
    def __new__(cls, *args, **kwargs):  # 構造方法,構造了物件的空間
        print("執行了__new__方法")
        return object.__new__(cls)  # 或者super().__new(cls) # 呼叫了object類中的__new__方法

    def __init__(self, name, age):  # 初始化方法
        print("執行了init方法")
        self.name = name
        self.age = age


c1 = Fuu("小白", 18)
"""
就這樣執行可以打印出:
執行了__new__方法
執行了init方法
"""

從上面的例子,我們可以更加清晰的得到初始化一個物件所經歷的過程:

  1、例項化Fuu類,應該先執行其__new__方法。

  2、但是Fuu類中沒有定義__new__方法,所以到object父類中執行object的__new__方法。

    回顧呼叫父類的兩種方法:

      object.__new__(cls)

      super().__new__(cls)

  3、此時在記憶體中開闢一塊物件空間。

  4、才執行init初始化方法,把地址傳給self,給物件封裝屬性。

總結:構造方法__new__和初始化方法__init__之間的區別?

形象的說,類好比一個人型別的模板,__new__構造方法就是捏小人的過程(捏出來的每一個小人都是一樣的),__init__初始化方法好比給小人穿衣服的過程(為物件封裝屬性),一定是先有__new__方法,後有__init__。

單例模式:一個類只有一個例項的時候,這種就叫做單例模式。

# 單例模式:一個類只有一個例項。
# 思考:如果使得一個類只能例項化一次,也就是這要控制只能開闢一次空間,就能實現單例模式。


class Singleinstance():
    __INSTANCE = None   # 通過設定一個標誌位,用來使得只能執行一次new方法

    def __new__(cls, *args, **kwargs):
        if not cls.__INSTANCE:
            cls.__INSTANCE = object.__new__(cls)
        return cls.__INSTANCE

    def __init__(self, name, age):
        self.name = name
        self.age = age

obj1 = Singleinstance('小白', 18)
obj2 = Singleinstance('小黑', 20)

print(obj1.name, obj1.age)
print(obj2.name, obj2.age)
print(obj1)
print(obj2)

>>>
小黑 20
小黑 20
<__main__.Singleinstance object at 0x000001C1657F6048>
<__main__.Singleinstance object at 0x000001C1657F6048>

'''
總結:由上列印可以看到不論例項化多少物件,都是一個物件,都會已最後一個為準。
'''

__str__ 與 __repr__

__str__

如果想把一個類的例項變成str型別,列印物件名的時候就執行__str__方法。

__str__ : str(obj),要求必須實現了__str__,要求這個方法的返回值必須是字串str型別

三種場景會觸發__str__方法:

  • (1)當你列印一個物件名的時候,就會就會觸發__str__。  
  • (2)當你使用%s格式化輸出物件的時候,也會觸發__str__。
  • (3)強制轉換資料型別的時候,也會觸發__str__。
class Student:
    def __init__(self, name, age, sex):
        self.name = name
        self.age = age
        self.sex = sex

    def __str__(self):
        return "%s %s %s" % (self.name, self.age, self.sex)

st1 = Student("小白", 18, "")
print(st1)                  #1.列印物件名,自動觸發__str__方法
>>>
小白 18 男


student_list = []
student_list.append(st1)
for index, i in enumerate(student_list):
    print('%s %s'% (index, i))          #2.當使用%s格式化的時候,自動觸發__str__

>>>
0 小白 18 男

__repr__

1、__repr__是__str__方法的備胎,如果有__str__就使用__str__,否則執行__repr__。

# (1)同時存在__str__和__repr__兩個方法:
class Student:
    def __init__(self, name, age, sex):
        self.name = name
        self.age = age
        self.sex = sex

    def __str__(self):
        return "str: %s %s %s" % (self.name, self.age, self.sex)

    def __repr__(self):
        return "repr: %s %s %s" % (self.name, self.age, self.sex)


st1 = Student("小白", 18, "")
print(st1)
>>>
str: 小白 18# (2)只存在__repr__方法時,再次列印物件名:
class Student:
    def __init__(self, name, age, sex):
        self.name = name
        self.age = age
        self.sex = sex

    # 註釋掉__str__雙下方法
    # def __str__(self):
    #     return "str: %s %s %s" % (self.name, self.age, self.sex)

    def __repr__(self):
        return "repr: %s %s %s" % (self.name, self.age, self.sex)


st1 = Student("小白", 18, "")
print(st1)         # 當__str__沒有時,執行__repr__方法
>>>
repr: 小白 18 男

如果__repr__僅僅只是__str__的備胎,那麼它就沒有存在的意義了。所有__repr__還是有它自己的用武之地的時候:

2、如果使用內建函式repr(obj),或者通過%r格式化的時候,就會自動觸發__repr__方法,不管有沒有__str__,都呼叫__repr__。

st1 = Student("小白", 18, "")
print(repr(st1))
>>>
repr: 小白 18 男

小技巧:

python中之所有既有__str__,又有__repr__,__str__用於顯示給使用者看,__repr__用於顯示給開發人員看。 下面就有一個偷懶的小辦法:

__str__=__repr__

擴充套件知識:

class Fcc:
    def __str__(self):
        return "Fcc.str"

    def __repr__(self):
        return "Fcc.repr"


class Ecc(Fcc):
    def __str__(self):
        return "Ecc.str"

    def __repr__(self):
        return "Ecc.repr"


E1 = Ecc()
print(E1)
>>>
Ecc.str
'''
(1) 先去子類中查詢,先呼叫__str__方法。
(2) 如果把子類的__str__方法註釋掉,會去父類中查詢父類的__str__方法
(3) 如果把父類的__Str__的方法註釋掉,會再回到子類中執行備胎__repr__方法。
'''