本篇文章帶給大家的內容是關於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 派生类繼承描述的是子類別與父類別之間的關係,是一種什麼是什麼的關係。要找出這種關係,必須先抽象再繼承抽象即抽取類似或說比較像的部分。 抽象化分成兩個層次:
# ==========================第一部分 # 例如 # 猫可以:喵喵叫、吃、喝、拉、撒 # 狗可以:汪汪叫、吃、喝、拉、撒 # 如果我们要分别为猫和狗创建一个类,那么就需要为 猫 和 狗 实现他们所有的功能,伪代码如下: # 猫和狗有大量相同的内容 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的相同時
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 '''提示:用已經有的類建立一個新的類,這樣就重用了已經有的軟體中的一部分設置大部分大大生了編程工作量這就是常說的軟體重用,不僅可以重用自己的類也可以繼承別人的,比如標準函式庫,來客製化新的資料型別這樣就是大大縮短了軟體開發週期,對大型軟體開發來說意義重大;類別的繼承又分為:單繼承與多繼承
#單一繼承#
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 '__main__.Person'>, <class '__main__.Animal'>, <class 'object'>) #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 '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class '__main__.Base'>, <class 'object'>]為了實現繼承,Python會在MRO列表上從左到右開始查找基類,直到找到第一個符合這個屬性的類為止。 而這個MRO列表的構造是透過一個C3線性化演算法來實現的。我們不去深究這個演算法的數學原理,它其實就是合併所有父類別的MRO列表並遵循如下三條準則:子類別會先於父類別被找出多個父類別會根據它們在列表中的從坐到右順序被查找如果對下一個類別存在兩個合法的選擇,選擇第一個父類別抽象類別和接口類別
#做出一个良好的接口 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关键字#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:将类方法变成一个普通的函数#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__的某个方法 __getitem__ __setitem__ __delitem__输出
以上是Python中物件導向詳細介紹(程式碼範例)的詳細內容。更多資訊請關注PHP中文網其他相關文章!