首頁 >後端開發 >Python教學 >Python中物件導向詳細介紹(程式碼範例)

Python中物件導向詳細介紹(程式碼範例)

不言
不言轉載
2018-12-13 10:37:482148瀏覽

本篇文章帶給大家的內容是關於Python中物件導向詳細介紹(程式碼範例),有一定的參考價值,有需要的朋友可以參考一下,希望對你有幫助。

物件導向的三大特性:整合多型封裝
我們來學習在Python種三種特性的實作

##繼承

#继承demo
class Animal:
    def __init__ (self,kind,age,sex):
        self.kind = kind
        self.age = age
        self.sex = sex
class Person(Animal):
    pass
class Dog(Animal):
    pass

class Cat(Animal):
    pass
#Animal:父类 or 基类
#Person:子类 or 派生类

繼承描述的是子類別與父類別之間的關係,是一種什麼是什麼的關係。要找出這種關係,必須先抽象再繼承

抽象即抽取類似或說比較像的部分。

抽象化分成兩個層次:


1. 將歐巴馬與梅西這兩個物件比較像的部分抽取成類別;


2.將人,豬,狗這三個類別比較像的部分抽取成父類。

抽象最主要的作用是劃分類別(可以隔離關注點,降低複雜度)

繼承:是基於抽象的結果,透過程式語言去實現它,肯定是先經歷抽象這個過程,才能透過繼承的方式去表達出抽象的結構。

抽像只是分析和設計的過程中,一個動作或者說一種技巧,透過抽象可以得到

# ==========================第一部分
# 例如
# 猫可以:喵喵叫、吃、喝、拉、撒
# 狗可以:汪汪叫、吃、喝、拉、撒
# 如果我们要分别为猫和狗创建一个类,那么就需要为 猫 和 狗 实现他们所有的功能,伪代码如下:

# 猫和狗有大量相同的内容
class cat:

    def 喵喵叫(self):
        print('喵喵叫')


    def 吃(self):
        print("吃东西")

    def 喝(self):
        print("喝水")

    def 拉(self):
        print("拉了")



class dog:

    def wangwang(self):
        print('旺旺叫')


    def 吃(self):
        print("吃东西")

    def 喝(self):
        print("喝水")

    def 拉(self):
        print("拉了")



#== == == == == == == == == == == == == 第二部分
#上述代码不难看出,吃、喝、拉、撒是猫和狗都具有的功能,而我们却分别的猫和狗的类中编写了两次,如果使用继承的思想,如下实现:
#动物:吃、喝、拉、撒
#猫:喵喵叫(猫继承动物的功能)
#狗:汪汪叫(狗继承动物的功能)

#伪代码如下:

class 动物:

    def 吃(self):
        print("吃东西")

    def 喝(self):
        print("喝水")

    def 拉(self):
        print("拉了")


# do something

# 在类后面括号中写入另外一个类名,表示当前类继承另外一个类
class cat(动物):
    def 喵喵叫(self):
        print("喵喵叫")


# 在类后面括号中写入另外一个类名,表示当前类继承另外一个类
class dog(动物):
        def 汪汪叫(self):
            print("汪汪叫")
#== == == == == == == == == == == == == 第三部分


# 继承的代码实现
class Animal:

    def eat(self):
        print("%s 吃 " % self.name)

    def drink(self):
        print("%s 喝 " % self.name)

    def shit(self):
        print("%s 拉 " % self.name)

    def pee(self):
        print("%s 撒 " % self.name)


class Cat(Animal):

    def __init__(self, name):
        self.name = name
        self.breed = '猫'

    def cry(self):
        print('喵喵叫')


class Dog(Animal):

    def __init__(self, name):
        self.name = name
        self.breed = '狗'

    def cry(self):
        print('汪汪叫')


########## 执行 #########

c1 = Cat('小白家的小黑猫')
c1.eat()

c2 = Cat('小黑的小白猫')
c2.drink()

d1 = Dog('胖子家的小瘦狗')
d1.eat()
繼承與重用性

在開發程式的過程中,如果我們定義了一個類別A,然後又想新建立另外一個類別B,但是類別B的大部分內容與類別A的相同時

我們不可能從頭開始寫一個類別B,這就用到了類的繼承的概念。

透過繼承的方式新類別B,讓B繼承A,B會'遺傳'A的所有屬性(資料屬性和函數屬性),實作程式碼重用

class Hero:
    def __init__(self,nickname,aggressivity,life_value):
        self.nickname=nickname
        self.aggressivity=aggressivity
        self.life_value=life_value

    def move_forward(self):
        print('%s move forward' %self.nickname)

    def move_backward(self):
        print('%s move backward' %self.nickname)

    def move_left(self):
        print('%s move forward' %self.nickname)

    def move_right(self):
        print('%s move forward' %self.nickname)

    def attack(self,enemy):
        enemy.life_value-=self.aggressivity
class Garen(Hero):
    pass

class Riven(Hero):
    pass

g1=Garen('草丛伦',100,300)
r1=Riven('锐雯雯',57,200)

print(g1.life_value)
r1.attack(g1)
print(g1.life_value)

'''
运行结果
243
'''
提示:用已經有的類建立一個新的類,這樣就重用了已經有的軟體中的一部分設置大部分大大生了編程工作量這就是常說的軟體重用,不僅可以重用自己的類也可以繼承別人的,比如標準函式庫,來客製化新的資料型別這樣就是大大縮短了軟體開發週期,對大型軟體開發來說意義重大;

類別的繼承又分為:單繼承與多繼承


#在學習單繼承與多繼承之前,在這裡我們先學習一下類別的型別


1、經典類別(不繼承object類別)

2.新式類別(繼承object類別)

3.Python2 經典類別與新式類別共存

4.Python3 全部都是新式類別

#單一繼承#

class Animal:
    a1 = "测试"
    def __init__(self,kind,age,sex):
        self.kind = kind
        self.age = age
        self.sex = sex
class Person(Animal):
    a1 = "alex"
    pass

class Dog(Animal):
    pass

class Cat(Animal):
    pass

a = Person("人类",18,"男")
print(a.kind)
我們了解了單繼承的語法,我們現在有這麼一個需求:

子類別有自己的屬性,如何要呼叫父類別的屬性和方法又要呼叫子類別的屬性和方法?

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

    def eat(self):
        print("%s正在吃饭" %(self.name))

class Person(Animal):
    def __init__(self,name,sex,age,coat_color):
        #super(Person,self).__init__(name,sex,age) python2写法
        super().__init__(name,sex,age)   #重构父类的__init__并给父类传递需要的参数,super()是一个特殊的对象默认会把self(对象自己)传递给父类当第一个参数(父类的self);
        self.coat_color = coat_color

    def eat(self):
        print("%s人类%s正在吃饭" % (self.coat_color,self.name))
        super().eat()

class Dog(Animal):
    def __init__(self,coat_color):
        self.coat_color = coat_color
        
    def eat(self):
        print("狗狗正在吃饭")

p2 = Person("王铁锤","女",18,"黄色")
p2.eat()
print(Person.__mro__)  #查看类的mro列表
>>>(<class &#39;__main__.Person&#39;>, <class &#39;__main__.Animal&#39;>, <class &#39;object&#39;>)
#super():是一个特殊对象,会按当前类所在mro列表中的位置的下一个类开始查找动态方法或静态属性

多重繼承

class Base:
    def __init__(self):
        print('Base.__init__')

class A(Base):
    def __init__(self):
        super().__init__()
        print('A.__init__')

class B(Base):
    def __init__(self):
        super().__init__()
        print('B.__init__')

class C(A,B):
    def __init__(self):
        super().__init__()  # Only one call to super() here
        print('C.__init__')
c = C()
print(C.mro())  #查看可的MON列表
#对于定义的每一个类,Python会计算出一个所谓的方法解析顺序(MRO)列表;这个MRO列表就是一个简单的所有基类的线性顺序表。例如:
print(C.mro())
>>>[<class &#39;__main__.C&#39;>, <class &#39;__main__.A&#39;>, <class &#39;__main__.B&#39;>, <class &#39;__main__.Base&#39;>, <class &#39;object&#39;>]
為了實現繼承,Python會在MRO列表上從左到右開始查找基類,直到找到第一個符合這個屬性的類為止。

而這個MRO列表的構造是透過一個C3線性化演算法來實現的。我們不去深究這個演算法的數學原理,它其實就是合併所有父類別的MRO列表並遵循如下三條準則:

子類別會先於父類別被找出

多個父類別會根據它們在列表中的從坐到右順序被查找

如果對下一個類別存在兩個合法的選擇,選擇第一個父類別

抽象類別和接口類別

在了解抽象類別之前我們先談談介面的問題,這裡的介面指的是:自己提供給使用者來呼叫自己功能的方式\方法\入口


那麼我們為什麼要使用接口呢?

我們提取一群類裡的類似的功能的函數,比如 微信支付 支付寶支付 銀聯支付等等這些都是支付的功能,我們可以把這些功能提取出來定義成一個類,這個類不具體實作功能只是定義這些功能的統一的名稱 例如交pay這麼做的意義在於歸一化,什麼叫歸一化,就是只要是基於同一個介面實現的類,那麼所有的這些類產生的對像在使用時,從用法上來說都一樣。


歸一化的好處在於:


1、歸一化讓使用者不需要關心物件的類別是什麼,只需要的知道這些物件都具備某些功能就可以了,這大大降低了使用者的使用難度。


2. 歸一化使得高層的外部使用者可以不加區分的處理所有介面相容的物件集合


2.1:好像linux的泛文件概念一樣,所有東西都可以當文件處理,不必關心它是內存、磁碟、網絡還是屏幕(當然,對底層設計者,當然也可以區分出“字符設備”和“塊設備”,然後做出針對性的設計:細緻到什麼程度,視需求而定)。


2.2:再例如:我們有一個汽車接口,裡面定義了汽車所有的功能,然後由本田汽車的類,奧迪汽車的類,大眾汽車的類,他們都實現了汽車接口,這樣就好辦了,大家只需要學會了怎麼開汽車,那麼無論是本田,還是奧迪,還是大眾我們都會開了,開的時候根本無需關心我開的是哪一類車,操作手法(函數呼叫)都一樣


#做出一个良好的接口
class Payment(object):
    #规定了一个兼容接口
    def pay(self):
        pass

#微信支付
class WeChatPay(object):
    def pay(self,money):
        print('微信支付了%s'%money)

#支付宝支付
class AliPay(object):
    def pay(self,money):
        print('支付宝支付了%s'%money)

#苹果支付
class ApplePay(object):
    def pay(self,money):
        print('苹果支付了%s'%money)

def pay(obj,money):
    obj.pay(money)

weixin = WeChatPay()
alipay = AliPay()
applepay = ApplePay()

#调用者无需关心具体实现细节,可以一视同仁的处理实现了特定接口的所有对象
pay(weixin,100)
pay(alipay,200)
pay(applepay,300)

归一化使得高层的外部使用者可以不加区分的处理所有接口兼容的对象集合——就好象linux的泛文件概念一样,所有东西都可以当文件处理,不必关心它是内存、磁盘、网络还是屏幕(当然,对底层设计者,当然也可以区分出“字符设备”和“块设备”,然后做出针对性的设计:细致到什么程度,视需求而定)

抽象类

什么是抽象类

与java一样,python也有抽象类的概念但是同样需要借助模块实现,抽象类是一个特殊的类,它的特殊之处在于只能被继承,不能被实例化

为什么要有抽象类

如果说类是从一堆对象中抽取相同的内容而来的,那么抽象类就是从一堆类中抽取相同的内容而来的,内容包括数据属性和函数属性。

比如我们有香蕉的类,有苹果的类,有桃子的类,从这些类抽取相同的内容就是水果这个抽象的类,你吃水果时,要么是吃一个具体的香蕉,要么是吃一个具体的桃子。。。。。。你永远无法吃到一个叫做水果的东西。

从设计角度去看,如果类是从现实对象抽象而来的,那么抽象类就是基于类抽象而来的。

从实现角度来看,抽象类与普通类的不同之处在于:抽象类中有抽象方法,该类不能被实例化,只能被继承,且子类必须实现抽象方法。这一点与接口有点类似,但其实是不同的,即将揭晓答案

import abc

class Animal(metaclass=abc.ABCMeta): #metaclass=abc.ABCMeta:元类
    @abc.abstractmethod
    def eat(self):
        pass
    @abc.abstractmethod
    def run(self):
        pass

class People(Animal):
    def eat(self):
        print("pople is eating")
    def run(self):
        print("pople is runing")


a = People()
a.eat()

抽象类和抽象接口

抽象类的本质还是类,指的是一组类的相似性,包括数据属性(如all_type)和函数属性(如read、write),而接口只强调函数属性的相似性。

抽象类是一个介于类和接口直接的一个概念,同时具备类和接口的部分特性,可以用来实现归一化设计

在python中,并没有接口类这种东西,即便不通过专门的模块定义接口,我们也应该有一些基本的概念

多态

Python默认支持多态

多态指的是一类事物有多种形态

动物有多种形态:人,狗,猪
import abc
class Animal(metaclass=abc.ABCMeta): #同一类事物:动物
    @abc.abstractmethod
    def talk(self):
        pass

class People(Animal): #动物的形态之一:人
    def talk(self):
        print('say hello')

class Dog(Animal): #动物的形态之二:狗
    def talk(self):
        print('say wangwang')

class Pig(Animal): #动物的形态之三:猪
    def talk(self):
        print('say aoao')

p1 = People()
d1 = Dog()
p2 = Pig()
p1.talk()
p2.talk()
d1.talk()
#多态性:在不考虑对象具体类型的情况下直接使用对象下的方法,比如列表 字符串 元祖等可以不考虑对象的类型直接调对应的方法,比如上边的代码无论是人或者狗或者猪我们不需要考虑这个类具体的类型因为他们都属于动物类,那么动物类都用talk方法

#我们上边学到了接口的概念那么我们在这里就可以用到将三个雷进行统一化
def talk(obj):
    obj.talk()
talk(p1)
talk(p2)
talk(d1)

鸭子类型

#python推崇的鸭子类型而不是抽象类的概念
#当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就可以被称为鸭子;sqqqqqqcvx b
#在鸭子类型中关注的不是对象的类型本身,而是它是如何使用的;
#在定义类的时候没有必要硬性定义一个父类,比如下面的代码三个类不需要父类,只需要都有发出声音的方法就行,某一个类的增加功能不需要考虑其他类实现了解耦;

import abc
class Pig():
    def speak(self):
        print("哼哼")
        
    
class Dog():
    def speak(self):
        print("汪汪")

class Radio():
    def speak(self):
        print("radio speak")

封装

#广义的封装:将一些内容放到一个"容器"中
#狭义的封装:私有
class test:
    name = "jim"
    __name = "tom" #私有属性
    
    def func(self):  #动态方法
        pass
        
    def __init__(self): #特殊方法
        self.name 
        self.__age = age #私有对象属性
        
    def _func(self): #私有方法
        pass
        
    @property  #属性:将方法伪装成一个属性,虽然在代码层面没有提升,但是会让代码看起来更合理;
    
    def func1(self):
        pass
    @classmethod #类方法(用于对于类的修改)
    def func2(cls):  #cls会接收类名类似self接收对象名一样
        pass
        
    @staticmethod #静态方法:不依赖类与对象的函数,封装在类中用于代码整洁一体化
    def func3():
        pass

私有成员:私有变量 私有对象属性 私有方法

对于每一个类的成员而言都有两种形式:
公有成员,在任何地方都能访问
私有成员,只有在类的内部才能方法
私有成员和公有成员的访问限制不同

静态变量

公有静态字段:类可以访问;类内部可以访问;派生类中可以访问
私有静态字段:仅类内部可以访问;

对象属性

公有普通字段:对象可以访问;类内部可以访问;派生类中可以访问;
私有普通字段:仅类内部可以访问;

方法:

公有方法:对象可以访问;类内部可以访问;派生类中可以访问
私有方法:仅类内部可以访问;

class test:
    name = "jim"     #公有静态属性
    __name = "tom"   #私有静态属性
    
    def __init__(self,): 
        self.name = name #公有对象属性
        self.__age = age #私有对象属性
        
    def _func(self): #私有方法
        pass
    
    def test(self):  #公有方法
        pass
    
class A:
    count = "china"
    __name = "alex"

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

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

    def __fuck(self):
        pass
obj = A("jim", 28)
print(obj.coutry)  # 类外可以访问
print(obj.__name)  # 类外不可以访问

#总结:

#对于这些私有成员来说,他们只能在类的内部使用,不能再类的外部以及派生类中使用.

#ps:非要访问私有成员的话,可以通过 对象._类__属性名,但是绝对不允许!!!

#为什么可以通过._类__私有成员名访问呢?因为类在创建时,如果遇到了私有成员(包括私有静态字段,私有普通字段,私有方法)它会将其保存在内存时自动在前面加上_类名.

#__开头的属性只是一种语法意义上的变形,并不会真正限制外部访问;

#这种变形只在类定义阶段发生一次,类定义之后再新增的属性不会变形;

封装数据属性的意义:将静态变量或对象变量封装起来以后,对象无法直接使用只能在类中使用这又有什么用呢?

1.将属性封装了以后对象通过统一的类方法(接口)才能增加静态属性,并且根据对象的传值的合法性来决定是否给用户增加静态属性,这就有意义了对于某些特殊的属性我们就需要通过统一的接口才能修改,并且遵循接口的定义者的规范;

class Person:
    def __init__(self):
        pass

    def eat(self):
        print("%s人类%s正在吃饭" % (self.coat_color, self.__name))

    def set_info(self,name,age,sex,coat_color):
        if type(name) is not str:
            raise TabError("name not is str")
        elif type(age) is not int:
            raise TabError("name not is int")
        else:
            self.__name = name
            self.__age = age
            self.__sex = sex
            self.coat_color = coat_color

p = Person()
p.set_info("jim",18,"男人","黄种")
p.eat()
#raise:自定义触发异常触发异常后,后面的代码就不会再执行

封装函数属性的意义:隔离复杂度

1. 电视机本身是一个黑盒子,隐藏了所有细节,但是一定会对外提供了一堆按钮,这些按钮也正是接口的概念,所以说,封装并不是单纯意义的隐藏!!!

2. 快门就是傻瓜相机为傻瓜们提供的方法,该方法将内部复杂的照相功能都隐藏起来了

提示:在编程语言里,对外提供的接口(接口可理解为了一个入口),可以是函数,称为接口函数,这与接口的概念还不一样,接口代表一组接口函数的集合体。

#取款是功能,而这个功能有很多功能组成:插卡、密码认证、输入金额、打印账单、取钱
#对使用者来说,只需要知道取款这个功能即可,其余功能我们都可以隐藏起来,很明显这么做
#隔离了复杂度,同时也提升了安全性

class ATM:
    def __card(self):
        print('插卡')
    def __auth(self):
        print('用户认证')
    def __input(self):
        print('输入取款金额')
    def __print_bill(self):
        print('打印账单')
    def __take_money(self):
        print('取款')

    def withdraw(self):
        self.__card()
        self.__auth()
        self.__input()
        self.__print_bill()
        self.__take_money()

a=ATM()
a.withdraw()

property

@property:property是一种特殊的属性,访问它时会执行一段功能(函数)然后返回值

使用场景:对象需要得到一个动态的返回值,并且调用方式与调用数据属性一样的方式调用

class People:
    def __init__(self,name,age,height,weight):
        self.name = name
        self.age = age
        self.height = height
        self.weight = weight
    @property
    def bim(self):
        return self.weight / (self.height ** 2 )

p1 = People("jim",28,1.8,72)
print(p1.bim)       #通过@property装饰的方法改变了调用方式
print(p1.name)      #调用普通的数据属性

class test:
    def __init__(self,name):
        self.__name = name
    @property            #@property:将动态方法封装成数据属性(常用)
    def name(self):
        return self.__name
    @name.setter         #.setter修改属性
    def name(self,obj):
        self.__name = obj

    @name.deleter       #.deleter删除属性
    def name(self):
        del self.__name
a = test("jim")
print(a.name)
a.name="tom"

为什么要用property

将一个类的函数定义成特性以后,对象再去使用的时候obj.name,根本无法察觉自己的name是执行了一个函数然后计算出来的,这种特性的使用方式遵循了统一访问的原则

classmethod

@classmethod:是一个内置的函数,我们了解类里的动态方法是给对象使用的,所以在定义方法时会自动有一个self关键字
这个关键字就是一个普通的位置参数而已,默认对象在调用方法时会自动将对象的内存地址传递给self这样我们就可以调用对象的属性
@classmethod 这个函数就是将一个普通类方法绑定给类使用,这样这个方法就需要通过类调用,而cls这个关键字就会默认接收类的内存地址
下面的方法还是普通的方法,不过是经过@classmethod装饰后,这个普通方法就通过cls这个关键字接收的不是对象的地址而是类的地址而已
还能通过类似cls.name = "tom" 修改类的静态属性
#settings.py
HOST="127.0.0.1"
PORT=3306

#classmethod_mode.py
import settings
class test:
    def __init__(self,host,port):
        self.host = host
        self.port = port

    def test1(self):
        print(self.host,self.port)

    @classmethod
    def test2(cls):
        print(cls)
        return cls(settings.PORT,settings.HOST)

test.test2()
a = test(settings.PORT,settings.HOST)
a.test1()

staticmethod

@staticmethod:将类方法变成一个普通的函数
我们有这么一个核心文件里面是很多类,但这个时候我们需要写一个普通函数实现某个功能
在类外面也可以写一个同样的函数实现功能,但是这样打乱了逻辑关系,也增加了代码的维护难度
那么我们就写在某个公共类里并且通过@staticmethod装饰一下这样类就会知道它只是一个我类里面定义的普通函数
主要是为了代码整洁归类
#settings.py
HOST="127.0.0.1"
PORT=3306

#classmethod_mode.py
import settings
class test:
    def __init__(self,host,port):
        self.host = host
        self.port = port

    def test1(self):
        print(self.host,self.port)

    @staticmethod
    def test2():
        return (settings.PORT,settings.HOST)

print(test.test2())

#小补充

#isinstance:什么是否是什么实例
l = [1,2,3]
print(isinstance(l,list)) #l是不是列表
#issubclass:什么是谁的子类
print(issubclass(b,a) #True
issubclass:判断b是否是b的子类或孙类

反射:通过字符串操作对象(实例化 类 模块)

有一天我们想通过字符串来对实例 类 或者模块进行某些操作,比如我们通过input()接收的用户输入的信息
这个信息默认是字符串格式,那么我们拿到了这个字符串能通过它来调用对象的属性或类的方法呢?
当然可以这就是反射 把某个字符串反射到某个对象属性或类方法的内存地址上去
反射一共有四个内置函数,可以操作实例化对象 类 模块;
  • hasattr():查找

  • getattr():获取

  • setattr():修改

  • delattr():删除

对实例化对象的示例

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

obj = a("jim",28)

ret = getattr(obj,"name",None)
print(ret)

print(hasattr(obj,"name"))

if hasattr(obj,name):
    ret = getattr(obj,"name",None)

setattr(obj,"sex","男")
print(getattr(obj,"sex"))

对类

class a:
    name = "kom"
    def __init__(self):
        pass
    
    def func(self):
        print("in func")
        
getattr(a,"name")
ret = getattr(a,"func")
print(ret(1))

对当前模块(文件)

import sys
def s1():
    print 's1'


def s2():
    print 's2'

this_module = sys.modules[__name__]

hasattr(this_module, 's1')
getattr(this_module, 's2')

对其他模块(文件)

import fs
print(getattr(fs,"n1"))

#方法1
a = getattr(fs,"A")
print(a.name)

#方法2
print(getattr(fs.A,"name"))


a.func2(1)

双下方法

class test:
    def __init__(self,name,age)
        self.name = name
        self.age = age
    
    def __str__(self):  #__str__方法:在打印对象时默认返回出该方法的返回值(只能是字符串,可以是格式化后的字符串)
        return "%s,%s" %(self.name,self,age)

    def __repr__(self):  #__repr__方法:在repr(对象)时默认输出该方法的返回值
        return "太白"

a = test("jim",27)
print(a)   

class Foo:
    def __init__(self):
        pass

    def __call__(self, *args, **kwargs): #__call__:对象()自动触发__call__() 方法
    print("__call__")


obj = Foo()
obj()  # 对象()自动触发__call__()方法

#__del__方法:当内存中释放时自动触发,主要用于主动关闭mysql连接或关闭文件等操作;

#__init__方法:负责对象的初始化,在初始化前对象已经产生
#__new__方法:属于新式类里的方法,第一个参数是(cls),该方法在实例化的时候会自动调用用于创建对象,并将返回值返回给__init__方法的self参数;

#模拟重构__new__方法
class test:
    def __init__(self):
        print("is init")

    def __new__(cls, *args, **kwargs):  #__new__:对象的产生者
        print("is new")
        #super().__new__(cls)
        return object.__new__(cls)
a = test()


class test2:
    def __init__(self):
        print("__init__ ")
        print(self)
        super(A, self).__init__()

    def __new__(cls):
        print("__new__ ")
        self = super(A, cls).__new__(cls)
        print(self)
        return self
#输出
__new__ 
<__main__.A object at 0x1046c4a20>
__init__ 
<__main__.A object at 0x1046c4a20>
#从输出结果来看,__new__ 方法的返回值就是类的实例对象,这个实例对象会传递给 __init__ 方法中的self参数,以便实例对象可以被正确地初始化;


#__init__方法中除了self之外定义的参数,都将与__new__方法中除cls参数之外的参数是必须保持一致
class B:
    def __init__(self, *args, **kwargs):
        print("init", args, kwargs)

    def __new__(cls, *args, **kwargs):
        print("new", args, kwargs)
        return super().__new__(cls)

B(1, 2, 3)
# 输出
new (1, 2, 3) {}
init (1, 2, 3) {}

设计模式

单例模式

由于类产生实例的过程是通过 __new__ 方法来控制的,因此重写该方法来单例模式是非常方便的;
每次初始化都返回同一个实例,所以两次初始化得到的对象的内存地址应该是一样的
class a:
    __instance = None
    def __new__(cls, *args, **kwargs):
        if cls.__instance is None:
            obj = object.__new__(cls)
            cls.__instance = obj
        return cls.__instance

cc = a()
bb = a()
print(cc,bb)
#输出
<__main__.a object at 0x105513898> <__main__.a object at 0x105513898>

item

#对一个对象进行类似字典的操作,就会触发__item__的某个方法
__getitem__
__setitem__
__delitem__
输出

以上是Python中物件導向詳細介紹(程式碼範例)的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:segmentfault.com。如有侵權,請聯絡admin@php.cn刪除