1. 程式人生 > 實用技巧 >面向物件初始

面向物件初始

內容詳細

1.面向物件基本格式

# ###### 定義類 ###### 
class 類名:
    def 方法名(self,name):
        print(name)
        return 123
    def 方法名(self,name):
        print(name)
        return 123
    def 方法名(self,name):
        print(name)
        return 123
# ###### 呼叫類中的方法 ###### 
# 1.建立該類的物件
obj = 類名()
# 2.通過物件呼叫方法
result = obj.方法名('alex')
print(result)

應用場景:遇到很多函式,需要給函式進行歸類和劃分。 【封裝】

2.物件的作用

儲存一些值,以後方便自己使用。

class File:
    def read(self):
        with open(self.xxxxx, mode='r', encoding='utf-8') as f:
            data = f.read()
        return data

    def write(self, content):
        with open(self.xxxxx, mode='a', encoding='utf-8') as f:
            f.write(content)

# # 例項化了一個File類的物件
obj1 = File()
# # 在物件中寫了一個xxxxx = 'test.log'
obj1.xxxxx = "test.log"
# # 通過物件呼叫類中的read方法,read方法中的self就是obj。
# # obj1.read()
obj1.write('alex')


# 例項化了一個File類的物件
obj2 = File()
# 在物件中寫了一個xxxxx = 'test.log'
obj2.xxxxx = "info.txt"
# 通過物件呼叫類中的read方法,read方法中的self就是obj。
# obj2.read()
obj2.write('alex')

class Person:
    def __init__(self,n,a,g): # 初始化方法(構造方法),給物件的內部做初始化。
        self.name = n
        self.age = a
        self.gender = g

    def show(self):
        temp = "我是%s,年齡:%s,性別:%s " % (self.name, self.age, self.gender,)
        print(temp)

# 類() 例項化物件,自動執行此類中的 __init__方法。
p1 = Person('李兆琪',19,'男')
p1.show()

p2 = Person('利奇航',19,'男')
p2.show()

總結:將資料封裝到物件,方便使用。

總結

"""
如果寫程式碼時,函式比較多比較亂。
1. 可以將函式歸類並放到同一個類中。
2. 函式如果有一個反覆使用的公共值,則可以放到物件中。
"""

class File:
    def __init__(self,path):
        self.file_path = path
        
    def read(self):
        print(self.file_path)
    
    def write(self,content):
        print(self.file_path)
    
    def delete(self):
        print(self.file_path)
    
    def update(self):
        print(self.file_path)
    
p1 = File('log.txt')
p1.read()

p2 = File('xxxxxx.txt')
p2.read()
# 1. 迴圈讓使用者輸入:使用者名稱/密碼/郵箱。 輸入完成後再進行資料列印。
# ########## 以前的寫法
USER_LIST = []
while True:
    user = input('請輸入使用者名稱:')
    pwd = input('請輸入密碼:')
    email = input('請輸入郵箱:')
    temp = {'username':user,'password':pwd,'email':email}
    USER_LIST.append(temp)
for item in USER_LIST:
    temp = "我的名字:%s,密碼:%s,郵箱%s" %(item['username'],item['password'],item['email'],)
    print(temp)
    
# ########## 面向物件寫法

class Person:
    def __init__(self,user,pwd,email):
        self.username = user
        self.password = pwd
        self.email = email
	
USER_LIST = [物件(使用者/密碼/郵箱),物件(使用者/密碼/郵箱),物件(使用者/密碼/郵箱)]
while True:
    user = input('請輸入使用者名稱:')
    pwd = input('請輸入密碼:')
    email = input('請輸入郵箱:')
    p = Person(user,pwd,email)
    USER_LIST.append(p)

for item in USER_LIST:
    temp = "我的名字:%s,密碼:%s,郵箱%s" %(item.username,item.password,item.email,)
    print(temp)

# ########## 面向物件寫法

class Person:
    def __init__(self,user,pwd,email):
        self.username = user
        self.password = pwd
        self.email = email
        
	def info(self):
        return "我的名字:%s,密碼:%s,郵箱%s" %(item.username,item.password,item.email,)
    
USER_LIST = [物件(使用者/密碼/郵箱),物件(使用者/密碼/郵箱),物件(使用者/密碼/郵箱)]
while True:
    user = input('請輸入使用者名稱:')
    pwd = input('請輸入密碼:')
    email = input('請輸入郵箱:')
    p = Person(user,pwd,email)
    USER_LIST.append(p)

for item in USER_LIST:
    msg = item.info()
    print(msg)

4.繼承

# 父類(基類)
class Base:
    def f1(self):
        pass
# 子類(派生類)
class Foo(Base):
    def f2(self):
        pass

# 建立了一個子類的物件
obj = Foo()
# 執行物件.方法時,優先在自己的類中找,如果沒有就是父類中找。
obj.f2()
obj.f1()

# 建立了一個父類的物件
obj = Base()
obj.f1()

問題:什麼時候才能用到繼承?多個類中如果有公共的方法,可以放到基類中避免重複編寫。

class Base:
    def f1(self):
        pass
    
class Foo(Base):
    def f2(self):
        pass
    
class Bar(Base):
    def f3(self):
        pass

obj1 = Foo()

obj2 = Bar()

繼承關係中的查詢方法的順序:

# 示例一
class Base:
    def f1(self):
        print('base.f1')
        
class Foo(Base):
    def f2(self):
        print('foo.f2')
        
obj = Foo()
obj.f1()
obj.f2()

# 示例二
class Base:
    def f1(self):
        print('base.f1')
        
class Foo(Base):
    def f2(self):
        self.f1()
        print('foo.f2')
        
obj = Foo()
obj.f2()

# 示例三
class Base:
    def f1(self):
        print('base.f1')
        
class Foo(Base):
    def f2(self):
        self.f1()
        print('foo.f2')
	def f1(self):
        print('foo.f1')
        
obj = Foo()
obj.f2()

# 示例四
class Base:
    def f1(self):
        self.f2()
        print('base.f1')
	def f2(self):
        print('base.f2')
class Foo(Base):
    def f2(self):
        print('foo.f2')
        
obj = Foo()
obj.f1()

# 示例五
class TCPServer:
    pass
class ThreadingMixIn:
    pass
class ThreadingTCPServer(ThreadingMixIn, TCPServer): 
    pass

# 示例六
class BaseServer:
    def serve_forever(self, poll_interval=0.5):
        self._handle_request_noblock()
	def _handle_request_noblock(self):
        self.process_request(request, client_address)
        
	def process_request(self, request, client_address):
        pass
    
class TCPServer(BaseServer):
    pass

class ThreadingMixIn:
    def process_request(self, request, client_address):
        pass
    
class ThreadingTCPServer(ThreadingMixIn, TCPServer): 
    pass

obj = ThreadingTCPServer()
obj.serve_forever()

注意事項:

  • self 到底是誰?
  • self 是哪個類建立的,就從此類開始找,自己沒有就找父類,同時繼承多個基類的就從左到右依次查詢,第一個基類找完了,沒找到,就到後續的基類中查詢。

5.多型(多種形態/多種型別)鴨子模型

# Python
def func(arg):
    v = arg[-1] # arg.append(9)
    print(v)

# java
def func(str arg):
    v = arg[-1]
    print(v)

面試題:什麼是鴨子模型。

對於一個函式而言,Python對於引數的型別不會限制,那麼傳入引數時就可以是各種型別,在函式中如果有例如:arg.send方法,那麼就是對於傳入型別的一個限制(型別必須有send方法)。這就是鴨子模型

總結

  1. 面向物件的三大特性:封裝/繼承/多型

    • 封裝

      class File:
          def read(self):
              pass
          def write(self):
              pass
      
      class Person:
          def __init__(sef,name,age):
              self.name = name
              self.age = age
      p = Person('alex',19)
      
    • 繼承

      class Base:
          pass
      class Foo(Base):
          pass
      
      • 多繼承
      • self到底是誰?
      • self是由於那個類建立,則找方法時候就從他開始找。
    • 多型

      def func(arg): # 多種型別,很多事物
          arg.send() # 必須具有send方法,呱呱叫
      
  2. 格式和關鍵詞

    class 類:
        def __init__(self,x):
            self.x = x 
            
        def 方法(self,name):
            print(self.x, name)
            
    # 例項化一個類的物件
    v1 = 類(666)
    v2.方法('alex')
    

    三個詞:

    • 物件
    • 方法
  3. 什麼時候用面向物件?

    • 函式(業務功能)比較多,可以使用面向物件來進行歸類。
    • 想要做資料封裝(建立字典儲存資料時,面向物件)。
    • 遊戲示例:建立一些角色並且根據角色需要再建立人物。

內容詳細

1.成員

    • 類變數
    • 繫結方法
    • 類方法
    • 靜態方法
    • 屬性
  • 例項(物件)

    • 例項變數
  • 定義:寫在類的下一級和方法同一級。

  • 訪問:

    類.類變數名稱
    物件.類變數名稱
    
  • 面試題

    class Base:
        x = 1  
    obj = Base()
    print(obj.x) # 先去物件中找,沒有再去類中找。
    obj.y = 123  # 在物件中添加了一個y=123的變數。
    print(obj.y)
    obj.x = 123
    print(obj.x)
    print(Base.x)
    
    class Parent:
      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
    Child2.x = 3
    print(Parent.x,Child1.x,Child2.x) # 1 2 3
    

總結:找變數優先找自己,自己沒有找 類 或 基類;修改或賦值只能在自己的內部設定。

1.3 方法(繫結方法/普通方法)

  • 定義:至少有一個self引數
  • 執行:先建立物件,由物件.方法()。 *也可以用類.方法()呼叫,不推薦
class Foo:
    def func(self,a,b):
        print(a,b)
        
obj = Foo()
obj.func(1,2)
# ###########################
class Foo:
    def __init__(self):
        self.name = 123

    def func(self, a, b):
        print(self.name, a, b)

obj = Foo()
obj.func(1, 2)

1.4 靜態方法

  • 定義:
    • @staticmethod裝飾器
    • 引數無限制
  • 執行:
    • 類.靜態方法名 ()
    • 物件.靜態方法() (不推薦)
class Foo:
    def __init__(self):
        self.name = 123

    def func(self, a, b):
        print(self.name, a, b)

    @staticmethod
    def f1():
        print(123)

obj = Foo()
obj.func(1, 2)

Foo.f1()
obj.f1() # 不推薦

1.5 類方法

  • 定義:
    • @classmethod裝飾器
    • 至少有cls引數,當前類。
  • 執行:
    • 類.類方法()
    • 物件.類方法() (不推薦)
class Foo:
    def __init__(self):
        self.name = 123

    def func(self, a, b):
        print(self.name, a, b)

    @staticmethod
    def f1():
        print(123)

    @classmethod
    def f2(cls,a,b):
        print('cls是當前類',cls)
        print(a,b)

obj = Foo()
obj.func(1, 2)

Foo.f1()
Foo.f2(1,2)

面試題:

# 問題: @classmethod和@staticmethod的區別?
"""
一個是類方法一個靜態方法。 
定義:
	類方法:用@classmethod做裝飾器且至少有一個cls引數。
	靜態方法:用staticmethod做裝飾器且引數無限制。
呼叫:
	類.方法直接呼叫。
	物件.方法也可以呼叫。 
"""

1.6 屬性

  • 定義:
    • @property裝飾器
    • 只有一個self引數
  • 執行:
    • 物件.方法 不用加括號。
class Foo:

    @property
    def func(self):
        print(123)
        return 666

obj = Foo()
result = obj.func
print(result)
# 屬性的應用

class Page:
    def __init__(self, total_count, current_page, per_page_count=10):
        self.total_count = total_count
        self.per_page_count = per_page_count
        self.current_page = current_page
    @property
    def start_index(self):
        return (self.current_page - 1) * self.per_page_count
    @property
    def end_index(self):
        return self.current_page * self.per_page_count


USER_LIST = []
for i in range(321):
    USER_LIST.append('alex-%s' % (i,))

# 請實現分頁展示:
current_page = int(input('請輸入要檢視的頁碼:'))
p = Page(321, current_page)
data_list = USER_LIST[p.start_index:p.end_index]
for item in data_list:
    print(item)
    
    
# 結合私有成員修飾符,可以將自己的資料做成介面,外部訪問時,修改的只是介面內容,而無法更改自己設定的內容

2.成員修飾符

  • 公有,所有地方都能訪問到。
  • 私有,只有自己可以訪問到。
class Foo:
    def __init__(self, name):
        self.__name = name

    def func(self):
        print(self.__name)


obj = Foo('alex')
# print(obj.__name)  # 訪問不到
obj.func() # 可以訪問
class Foo:
    __x = 1

    @staticmethod
    def func():
        print(Foo.__x)

# print(Foo.__x)  # 訪問不到
Foo.func()  # 可以訪問
class Foo:

    def __fun(self):
        print('msg')

    def show(self):
        self.__fun()

obj = Foo()
# obj.__fun()  # 訪問不到
obj.show()

3.小補充

class Foo:
    def __init__(self,num):
        self.num = num
        
        
cls_list = []
for i in range(10):
    cls_list.append(Foo)
    
for i in range(len(cls_list)):
    obj = cls_list[i](i)
    print(obj.num)
class Foo:
    def __init__(self,num):
        self.num = num
        
B = Foo
obj = B('alex')
class Foo:
	def f1(self):
        print('f1')
    
    def f2(self):
        print('f2')

obj = Foo()

v = [ obj.f1,obj.f2 ]
for item in v:
    item()
class Foo:
    def f1(self):
        print('f1')
    
    def f2(self):
        print('f2')
        
	def f3(self):
        v = [self.f1 , self.f2 ]
        for item in v:
            item()
            
obj = Foo()
obj.f3()
class Account:
    
    def login(self):
        pass
    
    def register(self):
        pass
    
    def run(self):
        info = {'1':self.register, '2':self.login }
        choice = input('請選擇:')
        method = info.get(choice)
        method()
class Foo:
    pass

class Foo(object):
    pass

# 在python3中這倆的寫法是一樣,因為所有的類預設都會繼承object類,全部都是新式類。

# 如果在python2中這樣定義,則稱其為:經典類
class Foo:
    pass 
# 如果在python2中這樣定義,則稱其為:新式類
class Foo(object):
    pass 

class Base(object):
    pass
class Bar(Base):
    pass

贈送

# 強制訪問私有成員

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


obj = Foo('alex')

print(obj._Foo__x) # 強制訪問私有例項變數

總結

  1. 資料封裝

  2. 繼承關係的查詢

  3. 巢狀

    class School(object):
        def __init__(self,title,addr):
            self.title = title
            self.address = addr
            
    class ClassRoom(object):
        
        def __init__(self,name,school_object):
            self.name = name
            self.school = school_object
            
    s1 = School('北京','沙河')
    s2 = School('上海','浦東')
    s3 = School('深圳','南山')
    
    c1 = ClassRoom('全棧21期',s1)
    c1.name
    c1.school.title
    c1.school.address
    # ############################################
    v = [11,22,33,{'name':'山海','addr':'浦東'}]
    
    v[0]
    v[3]['name']