天天看點

軒小陌的Python筆記-day18 面向對象進階day18 面向對象進階

day18 面向對象進階

軒小陌的Python筆記-day18 面向對象進階day18 面向對象進階

課程目标:掌握面向對象進階相關知識點,能溝通更加自如的使用面向對象來進行程式設計。

今日概要:

  • 成員
    • 變量
      • 執行個體變量
      • 類變量
    • 方法
      • 綁定方法
      • 類方法
      • 靜态方法
    • 屬性
  • 成員修飾符(公有/私有)
  • “對象嵌套”
  • 特殊成員

1.成員

面向對象中的所有成員如下:

  • 變量
    • 執行個體變量
    • 類變量
  • 方法
    • 綁定方法
    • 類方法
    • 靜态方法
  • 屬性

通過面向對象進行程式設計時,會遇到很多種情況,也會使用不同的成員來實作,接下來我們來逐一介紹成員特性和應用場景。

1.1 變量

  • 執行個體變量,屬于對象,每個對象中各自維護自己的資料。
  • 類變量,屬于類,可以被所有對象共享,一般用于給對象提供公共資料(類似于全局變量)。
軒小陌的Python筆記-day18 面向對象進階day18 面向對象進階
class Person(object):
    country = "中國"

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

    def show(self):
        message = "{}-{}-{}".format(self.country, self.name, self.age)
        print(message)

print(Person.country) 
>>輸出結果:中國

p1 = Person("軒小陌",20)
print(p1.name)
print(p1.age)
print(p1.country) 
p1.show() 
>>輸出結果:
軒小陌
20
中國
中國-軒小陌-20
           

提示:當每個對象中都存在的相同的執行個體變量時,可以選擇把它放在類變量中,這樣就可以避免在對象中維護多個相同資料。

易錯點 & 面試題

第1題:注意讀和寫的差別。

class Person(object):
    country = "中國"

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

    def show(self):
        message = "{}-{}-{}".format(self.country, self.name, self.age)
        print(message)

print(Person.country) 			# 中國

p1 = Person("軒小陌",20)
print(p1.name) 					# 軒小陌
print(p1.age) 					# 20
print(p1.country) 				# 中國
p1.show() 						# 中國-軒小陌-20

p1.name = "root"     			# 在對象p1中将name重新指派為root
p1.num = 19          			# 在對象p1中新增執行個體變量 num=19
p1.country = "China" 			# 在對象p1中新增執行個體變量 country="China"

print(p1.country)   			# China
print(Person.country) 			# 中國
           
class Person(object):
    country = "中國"

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

    def show(self):
        message = "{}-{}-{}".format(self.country, self.name, self.age)
        print(message)

print(Person.country) 			# 中國

Person.country = "美國"		  # 在Person類中獎country重新指派為"美國"


p1 = Person("軒小陌",20)
print(p1.name) 					# 軒小陌
print(p1.age) 					# 20
print(p1.country) 				# 美國
           

第2題:繼承關系中的讀寫

軒小陌的Python筆記-day18 面向對象進階day18 面向對象進階
class Base(object):
    country = "中國"

class Person(Base):

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

    def show(self):
        message = "{}-{}-{}".format(Person.country, self.name, self.age)
        # message = "{}-{}-{}".format(self.country, self.name, self.age)
        print(message)


# 讀
print(Base.country) 				# 中國
print(Person.country) 				# 中國

obj = Person("軒小陌",19)			  
print(obj.country) 					# 中國

# 寫
Base.country = "China"				# 将Base類中的country重新指派為'China'
Person.country = "泰國"			  # 在Person類中新增類變量country='泰國'
obj.country = "日本"				  # 在對象中obj中新增執行個體變量country='日本'
           

第3題:面試題

class Parent(object):
    x = 1

class Child1(Parent):
    pass

class Child2(Parent):
    pass

print(Parent.x, Child1.x, Child2.x) 
>>輸出結果:
1
1
1

Child1.x = 2
print(Parent.x, Child1.x, Child2.x) 
>>輸出結果:
1
2
1

Parent.x = 3
print(Parent.x, Child1.x, Child2.x) 
>>輸出結果:
3
2
3
           

1.2 方法

  • 綁定方法,預設有一個 self 參數,由對象進行調用(此時 self 等于調用方法的對象)【對象&類均可調用】
  • 類方法,預設有一個 cls 參數,用類或對象都可以調用(此時 cls 就等于調用方法的這個類)【對象&類均可調用】
  • 靜态方法,無預設參數,用類和對象都可以調用。【對象&類均可調用】
軒小陌的Python筆記-day18 面向對象進階day18 面向對象進階
class Foo(object):

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

    def f1(self):
        print("綁定方法", self.name)

    @classmethod
    def f2(cls):
        print("類方法", cls)

    @staticmethod
    def f3():
        print("靜态方法")
        

obj = Foo("軒小陌",20)

# 綁定方法(對象)
obj.f1() 或
Foo.f1(obj)
>>輸出結果:
綁定方法 軒小陌

# 類方法
Foo.f2() 或  	# cls就是目前調用這個方法的類。
obj.f2()  		 # cls就是目前調用這個方法的對象的類。
>>輸出結果:
類方法 <class '__main__.Foo'>

# 靜态方法
Foo.f3() 或      # 通過類執行方法
obj.f3() 		 # 通過對象執行方法
>>輸出結果:
靜态方法
           

在Python中比較靈活,方法都可以通過對象和類進行調用;而在java、c#等語言中,綁定方法隻能由對象調用;類方法或靜态方法隻能由類調用。

那麼,綁定方法,類方法和靜态方法分别在什麼場景下應用呢?其中,這主要取決于你的方法中是否需要用到參數,需要用到那種參數:

import os
import requests


class Download(object):

    def __init__(self, folder_path):
        self.folder_path = folder_path

    @staticmethod
    def download_dou_yin():
        # 下載下傳抖音
        res = requests.get('.....')

        with open("xxx.mp4", mode='wb') as f:
            f.write(res.content)

    def download_dou_yin_2(self):
        # 下載下傳抖音
        res = requests.get('.....')
        path = os.path.join(self.folder_path, 'xxx.mp4')
        with open(path, mode='wb') as f:
            f.write(res.content)


obj = Download("video")
obj.download_dou_yin()
           

面試題:在類中 @classmethod 和 @staticmethod 的作用?

1.3 屬性

屬性其實是由綁定方法 + 特殊裝飾器組合建立出來的,讓我們以後在調用方法時可以不加括号,例如:

class Foo(object):

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

    def f1(self):
        return 123

    @property
    def f2(self):
        return 123

obj = Foo("軒小陌")

v1 = obj.f1()
print(v1)

v2 = obj.f2
print(v2)
           

示例:以之前開發的分頁的功能為例:

class Pagination:
    def __init__(self, current_page, per_page_num=10):
        self.per_page_num = per_page_num

        if not current_page.isdecimal():
            self.current_page = 1
            return
        current_page = int(current_page)
        if current_page < 1:
            self.current_page = 1
            return
        self.current_page = current_page

    @property
    def start(self):
        return (self.current_page - 1) * self.per_page_num

    @property
    def end(self):
        return self.current_page * self.per_page_num


user_list = ["使用者-{}".format(i) for i in range(1, 3000)]

# 分頁顯示,每頁顯示10條
while True:
    page = input("請輸入頁碼:")

    pg_object = Pagination(page, 20)
    page_data_list = user_list[ pg_object.start : pg_object.end ]
    
    for item in page_data_list:
        print(item)
           

其實,除此之外,在很多子產品和架構的源碼中也有 porperty 的身影,例如:requests 子產品。

import requests

# 内部下載下傳視訊,并将下載下傳好的資料封裝到Response對象中。
res = requests.get(
    url="https://aweme.snssdk.com/aweme/v1/playwm/?video_id=v0200f240000buuer5aa4tij4gv6ajqg",
    headers={
        "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36 FS"
    }
)
           
軒小陌的Python筆記-day18 面向對象進階day18 面向對象進階

關于屬性的編寫有兩種方式:

  • 方式一,基于裝飾器
    class C(object):
        
        @property
        def x(self):
            print('property')
        
        @x.setter
        def x(self, value):
            print('setter', value)
        
        @x.deleter
        def x(self):
    		print('deleter')
            
    obj = C()
    
    obj.x
    obj.x = 123
    del obj.x
    >>輸出結果:
    property
    setter 123
    deleter
               
  • 方式二,基于定義變量
    class C(object):
        
        def getx(self): 
    		pass
        
        def setx(self, value): 
    		pass
            
        def delx(self): 
    		pass
            
        x = property(getx, setx, delx, "I'm the 'x' property.")
        
    obj = C()
    
    obj.x
    obj.x = 123
    del obj.x
               

Django 源碼一瞥:

class WSGIRequest(HttpRequest):
    def __init__(self, environ):
        script_name = get_script_name(environ)
        # If PATH_INFO is empty (e.g. accessing the SCRIPT_NAME URL without a
        # trailing slash), operate as if '/' was requested.
        path_info = get_path_info(environ) or '/'
        self.environ = environ
        self.path_info = path_info
        # be careful to only replace the first slash in the path because of
        # http://test/something and http://test//something being different as
        # stated in https://www.ietf.org/rfc/rfc2396.txt
        self.path = '%s/%s' % (script_name.rstrip('/'),
                               path_info.replace('/', '', 1))
        self.META = environ
        self.META['PATH_INFO'] = path_info
        self.META['SCRIPT_NAME'] = script_name
        self.method = environ['REQUEST_METHOD'].upper()
        # Set content_type, content_params, and encoding.
        self._set_content_type_params(environ)
        try:
            content_length = int(environ.get('CONTENT_LENGTH'))
        except (ValueError, TypeError):
            content_length = 0
        self._stream = LimitedStream(self.environ['wsgi.input'], content_length)
        self._read_started = False
        self.resolver_match = None

    def _get_scheme(self):
        return self.environ.get('wsgi.url_scheme')

    def _get_post(self):
        if not hasattr(self, '_post'):
            self._load_post_and_files()
        return self._post

    def _set_post(self, post):
        self._post = post

    @property
    def FILES(self):
        if not hasattr(self, '_files'):
            self._load_post_and_files()
        return self._files

    POST = property(_get_post, _set_post)
           

對屬性進行一個補充:

由于屬性和執行個體變量的調用方式相同,是以在編寫時需要注意:屬性名稱不要與執行個體變量重名。

class Foo(object):

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

    @property
    def func(self):
        return 123


obj = Foo("軒小陌", 123)
print(obj.name)
print(obj.func)
           

一旦重名,可能就會有報錯。

class Foo(object):

    def __init__(self, name, age):
        self.name = name  	# 報錯,系統會誤認為調用 @name.setter 裝飾的方法。
        self.age = age

    @property
    def name(self):
        return "{}-{}".format(self.name, self.age)

obj = Foo("軒小陌", 123)
           
class Foo(object):

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

    @property
    def name(self):
        return "{}-{}".format(self.name, self.age) 		# 報錯,循環調用自己(直到層級太深報錯)

    @name.setter
    def name(self, value):
        print(value)

obj = Foo("軒小陌", 123)
print(obj.name)
           

如果想要在屬性和執行個體名稱上建立一些關系,可以在執行個體變量上加一個下劃線。

class Foo(object):

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

    @property
    def name(self):
        return "{}-{}".format(self._name, self.age)

obj = Foo("軒小陌", 123)
print(obj._name)
print(obj.name)
           

2.成員修飾符

Python中成員的修飾符就是指的是:公有、私有。

  • 公有,在任何地方都可以調用這個成員。
  • 私有,隻有在類的内部才可以調用該成員(成員是以兩個下劃線開頭,則表示該成員為私有)。

示例1:

class Foo(object):

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

    def get_data(self):
        return self.__name

    def get_age(self):
        return self.age

obj = Foo("軒小陌", 123)

# 公有成員
print(obj.age)
>>輸出結果:123
v1 = self.get_age()
print(v1)
>>輸出結果:軒小陌

# 私有成員
print(obj.__name) 	
>>輸出結果:報錯,由于name是私有成員,隻能在類中進行使用。
v2 = obj.get_data()
print(v2)
>>輸出結果:軒小陌
           

示例2:

class Foo(object):

    def get_age(self):
        print("公有的get_age")

    def __get_data(self):
        print("私有的__get_data方法")

    def proxy(self):
        print("公有的proxy")
        self.__get_data()

obj = Foo()
obj.get_age()
obj.proxy()
>>輸出結果:
公有的get_age
公有的proxy
私有的__get_data方法
           

示例3:

class Foo(object):

    @property
    def __name(self):
        print("私有的__name")

    @property
    def proxy(self):
        print("公有的proxy")
        self.__name
        return 1

obj = Foo()
v1 = obj.proxy
print(v1)
>>輸出結果:
公有的proxy
私有的__name
1
           

特别提醒:父類中的私有成員,子類無法繼承。

class Base(object):

    def __data(self):
        print("base.__data")

    def num(self):
        print("base.num")


class Foo(Base):

    def func(self):
        self.num()
        self.__data() 	# 報錯,不允許執行父類中的私有方法

obj = Foo()
obj.func()
>>輸出結果:
base.num
AttributeError: 'Foo' object has no attribute '_Foo__data'
           
class Base(object):

    def __data(self):
        print("base.__data")

    def num(self):
        print("base.num")
        self.__data()  

class Foo(Base):

    def func(self):
        self.num()

obj = Foo()
obj.func()
>>輸出結果:
base.num
base.__data
           

注意:一般來說私有成員是無法被外部調用的,但如果用一些特殊的文法也可以從外部進行調用(Flask源碼中有這種寫法,但不推薦這樣做)。

class Foo(object):

    def __init__(self):
        self.__num = 123
        self.age = 19

    def __msg(self):
        print(1234)

obj = Foo()
print(obj.age)
print(obj._Foo__num)
obj._Foo__msg()
>>輸出結果:
19
123
1234
           

那麼,什麼時候該使用公有,什麼時候該使用私有呢?這取決于成員是否可以作為獨立的功能暴露給外部,讓外部調用并使用。

  • 如果可以,使用公有。
  • 如果不可以,僅作為内部其他方法的一個輔助,使用私有。

3.對象嵌套

在基于面向對象進行程式設計時,對象之間可以存在各種各樣的關系,例如:組合、關聯、依賴等(Java中的稱呼),簡單來說就是各種嵌套。

下面我們就用示例來了解常見的嵌套的情景:

情景1:

class Student(object):
    """ 學生類 """

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

    def message(self):
        data = "我是一名學生,我叫:{},我今年{}歲".format(self.name, self.age)
        print(data)

s1 = Student("軒小陌", 19)
s2 = Student("Alex", 19)
s3 = Student("日天", 19)


class Classes(object):
    """ 班級類 """

    def __init__(self, title):
        self.title = title
        self.student_list = []

    def add_student(self, stu_object):
        self.student_list.append(stu_object)

    def add_students(self, stu_object_list):
        for stu in stu_object_list:
            self.add_student(stu)

    def show_members(self):
        for item in self.student_list:
            item.message()

c1 = Classes("三年二班")
c1.add_student(s1)
c1.add_students([s2, s3])

print(c1.title)
print(c1.student_list)
>>輸出結果:
三年二班
[
    <__main__.Student object at 0x00000208CB971940>, 
    <__main__.Student object at 0x00000208CB9718E0>, 
    <__main__.Student object at 0x00000208CB9717C0>
]
           

情景2:

class Student(object):
    """ 學生類 """

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

    def message(self):
        data = "我是一名{}班的學生,我叫:{},我今年{}歲".format(self.class_object.title, self.name, self.age)
        print(data)


class Classes(object):
    """ 班級類 """

    def __init__(self, title):
        self.title = title

c1 = Classes("Python全棧")
c2 = Classes("Linux雲計算")

user_object_list = [
    Student("軒小陌", 19, c1),
    Student("Alex", 19, c1),
    Student("日天", 19, c2)
]

for obj in user_object_list:
    print(obj.name,obj.age, obj.class_object.title)
>>輸出結果:
軒小陌 19 Python全棧
Alex 19 Python全棧
日天 19 Linux雲計算
           
軒小陌的Python筆記-day18 面向對象進階day18 面向對象進階

情景3:

class Student(object):
    """ 學生類 """

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

    def message(self):
        data = "我是一名{}班的學生,我叫:{},我今年{}歲".format(self.class_object.title, self.name, self.age)
        print(data)


class Classes(object):
    """ 班級類 """

    def __init__(self, title, school_object):
        self.title = title
        self.school_object = school_object


class School(object):
    """ 學校類 """

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

s1 = School("北京校區")
s2 = School("上海校區")

c1 = Classes("Python全棧", s1)
c2 = Classes("Linux雲計算", s2)

user_object_list = [
    Student("軒小陌", 19, c1),
    Student("Alex", 19, c1),
    Student("日天", 19, c2)
]
for obj in user_object_list:
    print(obj.name, obj.class_object.title ,  obj.class_object.school_object.name)
>>輸出結果:
軒小陌 Python全棧 北京校區
Alex Python全棧 北京校區
日天 上海校區 Linux雲計算
           
軒小陌的Python筆記-day18 面向對象進階day18 面向對象進階

4.特殊成員

在Python的類中存在一些特殊的方法,這些方法的格式為:

__方法__

,這種方法在内部均有特殊的含義,常見的特殊方法有:

4.1

__init__

,初始化方法

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

obj = Foo("軒小陌")
           

4.2

__new__

,構造方法

class Foo(object):
    def __init__(self, name):
        print("第二步:初始化對象,在空對象中建立資料")
        self.name = name

    def __new__(cls, *args, **kwargs):
        print("第一步:先建立空對象并傳回")
        return object.__new__(cls)

obj = Foo("軒小陌")
           

4.3

__call__

:執行個體化對象直接執行,會調用該方法。

class Foo(object):
    def __call__(self, *args, **kwargs):
        print("執行call方法")

obj = Foo()
obj()
           

4.4

__str__

:類中如果存在該方法,在輸出執行個體化對象時會自動執行并傳回值字元串。

class Foo(object):

    def __str__(self):
        return "哈哈哈哈"

obj = Foo()
print(obj)
           

4.5

__dict__

:擷取執行個體化對象中所有的示例變量并以字典的形式傳回。

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

obj = Foo("軒小陌",19)
print(obj.__dict__)
           

4.6

__getitem__

__setitem__

__delitem__

class Foo(object):

    def __getitem__(self, item):
        print(item)

    def __setitem__(self, key, value):
        print(key,value)

    def __delitem__(self, key):
        print(key)

obj = Foo()

obj["x1"]			# 自動觸發__geritem__方法
obj['x2'] = 123		# 自動觸發__setitem__方法
del obj['x3']		# 自動觸發__delitem__方法
>>輸出結果:
x1
x2 123
x3
           

4.7

__enter__

__exit__

:上下文管理方法,成對出現

class Foo(object):

    def __enter__(self):
        print("進入")
        return 666

    def __exit__(self, exc_type, exc_val, exc_tb):
        print("退出")

obj = Foo()
with obj as data:		# with+對象 as xx:類内部會自動執行__enter__方法
    print(data)			# 當with中的代碼執行完畢,類内部會自動執行__exit__方法
>>輸出結果:
進入
666 
退出
           

執行個體:

'''
資料連接配接:每次對遠端的資料進行操作時候都必須經過以下步驟:
1.連接配接 = 連接配接資料庫
2.操作資料庫
3.關閉連接配接
'''
class SqlHelper(object):

    def __enter__(self):
        self.連接配接 = 連接配接資料庫
        return 連接配接

    def __exit__(self, exc_type, exc_val, exc_tb):
        self.連接配接.關閉

        
with SqlHelper() as 連接配接:
    連接配接.操作...
    
with SqlHelper() as 連接配接:
    連接配接.操作...
           

面試題(補充代碼,實作如下功能)

class Context:
    
    def __enter__(self):
        return self
    
    def do_something(self):
        print('内部執行')
    
    def __exit__(self, exc_type, exc_val, exc_tb):
        pass

with Context() as ctx:
    print('内部執行')
    ctx.do_something()
           

4.8

__add__

__sub__

__mul__

__truediv__

等。

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

    def __add__(self, other):
        return "{}-{}".format(self.name, other.name)

v1 = Foo("alex")
v2 = Foo("sb")
v3 = v1 + v2	# 對象+值:内部會執行對象.__add__方法,并将+後面的值當做參數傳遞進去。
print(v3)
>>輸出結果:
alex-sb
           

4.9

__iter__

4.9.1 疊代器

'''
疊代器類型的定義:
    1.當類中定義了 __iter__ 和 __next__ 兩個方法。
    2.__iter__ 方法需要傳回對象本身,即:self
    3.__next__ 方法,傳回下一個資料,如果不傳回資料,則需要抛出一個StopIteration的異常。
	官方文檔:https://docs.python.org/3/library/stdtypes.html#iterator-types
'''
        
# 建立疊代器類型:
class IT(object):
    def __init__(self):
        self.counter = 0

    def __iter__(self):
        return self

    def __next__(self):
        self.counter += 1
        if self.counter == 3:
            raise StopIteration()
        return self.counter

# 根據類執行個體化建立一個疊代器對象:
obj1 = IT()
    
v1 = next(obj1)
print(v1)
>>輸出結果:1
v2 = next(obj1)
print(v2)
>>輸出結果:2
v3 = next(obj1)
print(v3)
>>輸出結果:StopIteration

obj2 = IT()
for item in obj2:  
    # 首先會執行疊代器對象的__iter__方法并擷取傳回obj2對象,然後傳回的對象會一直反複執行__next__方法并傳回給item
    print(item)
>>輸出結果:
1
2
           

疊代器對象支援通過__next__取值,如果取值結束則自動抛出StopIteration。

for内部循環時,先執行__iter__方法,擷取一個疊代器對象,然後不斷執行的__next__取值,有異常StopIteration則終止循環。

4.9.2 生成器

# 建立生成器函數
def func():
    yield 1
    yield 2
    
# 建立生成器對象(内部是根據生成器類generator建立的對象),生成器類的内部也聲明了__iter__和__next__方法。
obj1 = func()
    
v1 = next(obj1)
print(v1)

v2 = next(obj1)
print(v2)

v3 = next(obj1)
print(v3)

obj2 = func()
for item in obj2:
    print(item)
           

如果按照疊代器的規則來看,其實生成器類也是一種特殊的疊代器類。

4.9.3 可疊代對象

如果一個類中有__iter__方法且傳回一個疊代器對象,則稱以這個類建立的對象為可疊代對象。

class Foo(object):
    
    def __iter__(self):
        return 疊代器對象(生成器對象)
    
obj = Foo() # obj是 可疊代對象。

# 可疊代對象可以通過for進行循環,在循環的内部先執行__iter__方法,擷取其疊代器對象,然後再在内部執行該疊代器對象的__next__方法,逐漸取值。
for item in obj:
    pass
           

示例1:

class IT(object):			# 建立疊代器類
    def __init__(self):
        self.counter = 0

    def __iter__(self):
        return self

    def __next__(self):
        self.counter += 1
        if self.counter == 3:
            raise StopIteration()
        return self.counter

    
class Foo(object):			# 通過該類建立的對象為可疊代對象
    def __iter__(self):
        return IT()

obj = Foo() 				# 可疊代對象

for item in obj: 
    # 循環可疊代對象時,内部先執行obj.__iter__并擷取疊代器對象,然後再一直執行疊代器對象的__next__方法。
    print(item)
           

示例2:基于可疊代對象&疊代器實作:自定義range

class IterRange(object):
    def __init__(self, num):
        self.num = num
        self.counter = -1

    def __iter__(self):
        return self

    def __next__(self):
        self.counter += 1
        if self.counter == self.num:
            raise StopIteration()
        return self.counter


class Xrange(object):
    def __init__(self, max_num):
        self.max_num = max_num

    def __iter__(self):
        return IterRange(self.max_num)

obj = Xrange(100)

for item in obj:
    print(item)
           

示例3:基于可疊代對象&生成器 實作:自定義range

class Xrange(object):
    def __init__(self, max_num):
        self.max_num = max_num

    def __iter__(self):
        counter = 0
        while counter < self.max_num:
            yield counter
            counter += 1

obj = Xrange(100)
for item in obj:
    print(item)
           

常見的資料類型如清單、元組、集合、字典等也是可疊代對象:

v1 = [11,22,33,44]
v2 = (11,22,33,44)
v3 = {11,22,33,44}
v4 = {k1:11,k2:22}
# 以上均為可疊代對象,在其内部聲明了一個__iter__方法并且傳回一個疊代器對象。
           

判斷一個對象是否為可疊代對象/疊代器:

from collections.abc import Iterator, Iterable

v1 = [11, 22, 33]
print( isinstance(v1, Iterator) )  # 判斷是否是疊代器,依據是是否有__iter__和__next__
>>輸出結果:False

v2 = v1.__iter__()
print( isinstance(v2, Iterator) )  # 判斷是否是疊代器
>>輸出結果:True

v1 = [11, 22, 33]
print( isinstance(v1, Iterable) )  # 判斷是否可疊代,依據是是否有__iter__且傳回疊代器對象。
>>輸出結果:True

v2 = v1.__iter__()
print( isinstance(v2, Iterable) )  # 判斷是否可疊代,依據是是否有__iter__且傳回疊代器對象。
>>輸出結果:True

'''
isinstance(v1, Iterator)為True,則為疊代器
isinstance(v1, Iterator)為False,isinstance(v1, Iterable)為True,則為可疊代對象
'''
           

今日總結

  1. 面向對象程式設計中的成員
    • 變量
      • 執行個體變量
      • 類變量
    • 方法
      • 綁定方法
      • 類方法
      • 靜态方法
    • 屬性
  2. 成員修飾符
  3. 對象中的資料嵌套
  4. 特殊成員
  5. 重要概念:
    • 疊代器
    • 生成器
    • 可疊代對象

今日作業

  1. 列舉面向對象的成員并簡述他們的特點。
    1、變量:執行個體變量(通過對象調用,屬于對象),類變量(屬于類,可通過類和對象進行調用)
    2、方法:綁定方法(預設有self參數,self代指該對象,可通過對象和類進行調用,)、類方法(預設有cls參數,cls代指該類,可通過類和對象進行調用)、靜态方法(無預設參數,通過類和對象進行調用,本身與對象和類沒有直接關系)
    3、屬性(在方法前面進行聲明後,後續調用該方法時可通過特殊的形式進行調用)
               
  2. @staticmethod 和 @classmethod的作用是什麼?
    聲明靜态方法和類方法
               
  3. 面向對象中如何讓成員變為私有。
    在成員名前加上雙下劃線即可讓該成員程式設計私有
               
  4. __new__

    方法的作用?
    __new__是構造方法,在類建立對象時先執行該函數,建立空對象并傳回,然後再執行初始化方法
               
  5. 簡述你了解的:疊代器、生成器、可疊代對象。
    疊代器:類内部含有__iter__和__next__方法,可通過for循環先執行__iter__方法生成疊代器對象,再一直執行__next__方法擷取資料,直到遇到異常而結束循環。
    生成器:一種特殊的疊代器。
    可疊代對象:類内部含有__iter__方法,可通過for循環執行__iter__方法生成一個疊代器對象,再一直執行該疊代器對象對應的疊代器類中的__next__方法,直到遇到異常而結束循環。
               
  6. 看代碼寫結果
    class Foo(object):
        a1 = 1
        
        def __init__(self,num):
            self.num = num
            
        def show_data(self):
            print(self.num+self.a1)
        
    obj1 = Foo(666)
    obj2 = Foo(999)
    
    print(obj1.num)
    >>輸出結果:666
    print(obj1.a1)
    >>輸出結果:1
    
    obj1.num = 18
    obj1.a1 = 99
    
    print(obj1.num)
    >>輸出結果:18
    print(obj1.a1)
    >>輸出結果:99
    
    print(obj2.a1)
    >>輸出結果:1
    print(obj2.num)
    >>輸出結果:999
    print(Foo.a1)
    >>輸出結果:1
    print(obj1.a1)
    >>輸出結果:99
               
  7. 看代碼寫結果,注意傳回值。
    class Foo(object):
        
        def f1(self):
            return 999
        
        def f2(self):
            v = self.f1()
            print('f2')
            return v
        
        def f3(self):
            print('f3')
            return self.f2()
        
        def run(self):
            result = self.f3()
            print(result)
    
    obj = Foo()
    v1 = obj.run()
    print(v1)
    >>輸出結果:
    f3
    f2
    999
    None
               
  8. 看代碼寫結果【如果有錯誤,則标注錯誤即可,并且假設程式報錯可以繼續執行】
    class Foo(object):
        
        def f1(self):
            print('f1')
    
        @staticmethod
        def f2():
            print('f2')
            
    obj = Foo()
    obj.f1()
    >>輸出結果:f1
    obj.f2()
    >>輸出結果:f2
    Foo.f1()
    >>輸出結果:報錯,沒傳對象參數
    Foo.f2()
    >>輸出結果:f2
               
  9. 看代碼寫結果【如果有錯誤,則标注錯誤即可,并且假設程式報錯可以繼續執行】
    class Foo(object):
        
        def f1(self):
            print('f1')
            self.f2()
            self.f3()
    
        @classmethod
        def f2(cls):
            print('f2')
    
        @staticmethod
        def f3():
            print('f3')
    
    obj = Foo()
    obj.f1()
    >>輸出結果:
    f1
    f2
    f3
               
  10. 看代碼寫結果【如果有錯誤,則标注錯誤即可,并且假設程式報錯可以繼續執行】
    class Base(object):
        @classmethod
        def f2(cls):
            print('f2')
    
        @staticmethod
        def f3():
            print('f3')
    
    class Foo(Base):
        def f1(self):
            print('f1')
            self.f2()
            self.f3()
    
    obj = Foo()
    obj.f1()
    >>輸出結果:
    f1
    f2
    f3
               
  11. 看代碼寫結果【如果有錯誤,則标注錯誤即可,并且假設程式報錯可以繼續執行】
    class Foo(object):
        a1 = 1
        __a2 = 2
        
        def __init__(self,num):
            self.num = num
            self.__salary = 1000
            
        def show_data(self):
             print(self.num+self.a1)
        
    obj = Foo(666)
    
    print(obj.num)
    >>輸出結果:666
    print(obj.a1)
    >>輸出結果:1
    print(obj.__salary)
    >>輸出結果:報錯,私有成員隻能在類内部被調用
    print(obj.__a2)
    >>輸出結果:報錯,私有成員隻能在類内部被調用
    print(Foo.a1)
    >>輸出結果:1
    print(Foo.__a2)
    >>輸出結果:報錯,私有成員隻能在類内部被調用
    obj.show_data()
    667
               
  12. 看代碼寫結果
    class Foo(object):
    
        def __init__(self, age):
            self.age = age
    
        def display(self):
            print(self.age)
    
    
    data_list = [Foo(8), Foo(9)]
    
    for item in data_list:
        print(item.age, item.display())
    >>輸出結果:
    8
    8 None
    9
    9 None
               
  13. 看代碼寫結果
    class Base(object):
        def __init__(self, a1):
            self.a1 = a1
    
        def f2(self, arg):
            print(self.a1, arg)
    
    
    class Foo(Base):
        def f2(self, arg):
            print('666')
    
    obj_list = [Base(1), Foo(2), Foo(3)]
    
    for item in obj_list:
        item.f2(1)
    >>輸出結果:
    1 1
    666
    666
               
  14. 看代碼寫結果
    class Foo(object):
        def __init__(self, num):
            self.num = num
            
    v1 = [Foo for i in range(10)]
    v2 = [Foo(5) for i in range(10)]
    v3 = [Foo(i) for i in range(10)]
    
    print(v1)
    print(v2)
    print(v3)
    >>輸出結果:
    [
        <class '__main__.Foo'>, 
        <class '__main__.Foo'>, 
        <class '__main__.Foo'>, 
        <class '__main__.Foo'>, 
        <class '__main__.Foo'>, 
        <class '__main__.Foo'>, 
        <class '__main__.Foo'>, 
        <class '__main__.Foo'>, 
        <class '__main__.Foo'>, 
        <class '__main__.Foo'>
    ]
    [
        <__main__.Foo object at 0x0000015AB342BD60>, 
        <__main__.Foo object at 0x0000015AB342BCD0>, 
        <__main__.Foo object at 0x0000015AB3271A00>, 
        <__main__.Foo object at 0x0000015AB3271730>, 
        <__main__.Foo object at 0x0000015AB3271610>, 
        <__main__.Foo object at 0x0000015AB32714F0>, 
        <__main__.Foo object at 0x0000015AB3262E80>, 
        <__main__.Foo object at 0x0000015AB3262BE0>, 
        <__main__.Foo object at 0x0000015AB3262A90>, 
        <__main__.Foo object at 0x0000015AB3262A30>
    ]
    [
        <__main__.Foo object at 0x0000015AB3262850>, 
        <__main__.Foo object at 0x0000015AB32626A0>, 
        <__main__.Foo object at 0x0000015AB3262580>, 
        <__main__.Foo object at 0x0000015AB31F5F40>, 
        <__main__.Foo object at 0x0000015AB31F5220>, 
        <__main__.Foo object at 0x0000015AB31D51F0>, 
        <__main__.Foo object at 0x0000015AB31D5400>, 
        <__main__.Foo object at 0x0000015AB31D57F0>, 
        <__main__.Foo object at 0x0000015AB31D5790>, 
        <__main__.Foo object at 0x0000015AB31D5730>
    ]
               
  15. 看代碼寫結果
    class StarkConfig(object):
    
        def __init__(self, num):
            self.num = num
    
        def changelist(self, request):
            print(self.num, request)
    
    config_obj_list = [ StarkConfig(1),  StarkConfig(2),  StarkConfig(3) ]
    for item in config_obj_list:
        print(item.num)
    >>輸出結果:
    1
    2
    3
               
  16. 看代碼寫結果
    class StarkConfig(object):
    
        def __init__(self, num):
            self.num = num
    
        def changelist(self, request):
            print(self.num, request)
    
    config_obj_list = [StarkConfig(1), StarkConfig(2), StarkConfig(3)]
    for item in config_obj_list:
        item.changelist(666)
    >>輸出結果:
    1 666
    2 666
    3 666
               
  17. 看代碼寫結果
    class StarkConfig(object):
    
        def __init__(self, num):
            self.num = num
    
        def changelist(self, request):
            print(self.num, request)
    
        def run(self):
            self.changelist(999)
    
    
    class RoleConfig(StarkConfig):
    
        def changelist(self, request):
            print(666, self.num)
    
    
    class AdminSite(object):
        def __init__(self):
            self._registry = {}
    
        def register(self, k, v):
            self._registry[k] = v
    
    site = AdminSite()
    
    site.register('軒小陌', StarkConfig(19))
    site.register('root', StarkConfig(20))
    site.register("admin", RoleConfig(33))
    
    print(len(site._registry))
    >>輸出結果:3
    
    for k, row in site._registry.items():
        row.changelist(5)
    >>輸出結果:
    19 5
    20 5
    666 33
               
  18. 看代碼寫結果(如有報錯,請标注報錯位置)
    class StarkConfig(object):
        def __init__(self, num):
            self.num = num
        def run(self):
            self()
        def __call__(self, *args, **kwargs):
            print(self.num)
            
    class RoleConfig(StarkConfig):
        def __call__(self, *args, **kwargs):
            print(345)
        def __getitem__(self, item):
            return self.num[item]
        
    v1 = RoleConfig('alex')
    v2 = StarkConfig("xuanxiaomo")
    
    print(v1[1])
    print(v2[2])
    >>輸出結果:
    l
    報錯,StarkConfig類中沒有__getitem__方法,無法通過v2[2]調用
               
  19. 補全代碼
    class Context:
    	# 補充代碼
        def __enter__(self):
            return self
        
        def __exit__(self, exc_type, exc_val, exc_tb):
        	pass
    	
        def do_something(self):
            pass 
    
    with Context() as ctx:
        ctx.do_something()
               
  20. 看代碼寫結果
    class Department(object):
        def __init__(self,title):
            self.title = title
    
    class Person(object):
        def __init__(self,name,age,depart):
            self.name = name
            self.age = age 
            self.depart = depart
    	
        def message(self):
            msg = "我是%s,年齡%s,屬于%s" %(self.name,self.age,self.depart.title)
            print(msg)
        
        
    d1 = Department('人事部')
    d2 = Department('銷售部')
    
    p1 = Person('軒小陌',18,d1)
    p2 = Person('alex',18,d1)
    
    p1.message()
    p2.message()
    >>輸出結果:
    我是軒小陌,年齡18,屬于人事部
    我是alex,年齡18,屬于銷售部
               
  21. 分析代碼關系,并寫出正确的輸出結果。
    class Node(object):
        def __init__(self, title):
            self.title = title
            self.children = []
    
        def add(self, node):
            self.children.append(node)
    
        def __getitem__(self, item):
            return self.children[item]
    
    
    root = Node("中國")
    
    root.add(Node("河南省"))		
    root.add(Node("河北省"))		
    
    print(root.title)
    print(root[0])
    print(root[0].title)
    print(root[1])
    print(root[1].title)
    >>輸出結果:
    中國
    <__main__.Node object at 0x0000017737EE18B0>
    河南省
    <__main__.Node object at 0x0000017737EE1610>
    河北省
               
  22. 分析代碼關系,并寫出正确的輸出結果。
    class Node(object):
        def __init__(self, title):
            self.title = title
            self.children = []
    
        def add(self, node):
            self.children.append(node)
    
        def __getitem__(self, item):
            return self.children[item]
    
    
    root = Node("中國")
    
    root.add(Node("河南省"))		# root.children = [對象1]
    root.add(Node("河北省"))		# root.children = [對象1,對象2]
    root.add(Node("陝西省"))		# root.children = [對象1,對象2,對象3]
    root.add(Node("山東省"))		# root.children = [對象1,對象2,對象3,對象4]
    
    root[1].add(Node("石家莊"))	# 對象2.children = [對象5]
    root[1].add(Node("保定"))		 # 對象2.children = [對象5,對象6]
    root[1].add(Node("廊坊"))		 # 對象2.children = [對象5,對象6,對象7]
    
    root[3].add(Node("濰坊"))		 # 對象4.children = [對象8]
    root[3].add(Node("煙台"))		 # 對象4.children = [對象8,對象9]
    root[3].add(Node("威海"))		 # 對象4.children = [對象8,對象9,對象10]
    
    root[1][1].add(Node("雄安"))	 # 對象6.children = [對象11]
    root[1][1].add(Node("望都"))	 # 對象6.children = [對象11,對象12]
    
    print(root.title)
    print(root[0].title)
    print(root[1].title)
    print(root[1][0].title)
    print(root[1][2].title)
    print(root[1][1][0].title)
    >>輸出結果:
    中國
    河南省
    河北省
    石家莊
    廊坊
    雄安