이 글은 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. 사람, 돼지, 개 세 가지 범주 중 더 유사한 부분을 카테고리로 추출합니다. .
추상화의 주요 기능은 범주를 나누는 것입니다(관심사를 격리하고 복잡성을 줄일 수 있음)
# ==========================第一部分 # 例如 # 猫可以:喵喵叫、吃、喝、拉、撒 # 狗可以:汪汪叫、吃、喝、拉、撒 # 如果我们要分别为猫和狗创建一个类,那么就需要为 猫 和 狗 实现他们所有的功能,伪代码如下: # 猫和狗有大量相同的内容 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를 정의한 다음 만들고 싶다면 a new 또 다른 클래스 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. 클래식 클래스(객체 클래스를 상속하지 않음)
2. 새로운 스타일의 클래스(객체 클래스를 상속함)
3. 새로운 스타일의 클래스가 공존합니다
단일 상속
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 목록에서 기본 클래스를 왼쪽에서 오른쪽으로 검색합니다.
하위 클래스는 상위 클래스보다 먼저 발견됩니다.
여러 상위 클래스는 그에 따라 목록에 포함됩니다. 앉은 자세에서 오른쪽으로 검색 다음 클래스에 대한 합법적인 두 가지 선택이 있다면 첫 번째 상위 클래스를 선택하세요추상 클래스와 인터페이스 클래스추상 클래스를 이해하기 전에 먼저 인터페이스에 대해 이야기해 보겠습니다. 여기서 인터페이스는 메소드 입구를 의미합니다 사용자가 자신의 기능을 호출할 수 있도록 제공합니다
그렇다면 우리는 왜 인터페이스를 사용할까요?
WeChat 결제, Alipay 결제, UnionPay 결제 등과 같은 유사한 기능을 가진 기능을 추출합니다. 이러한 기능은 모두 추출하여 클래스로 정의할 수 있습니다. 결제 등의 기능을 통일한 이름은 정규화를 의미합니다. 정규화란 동일한 인터페이스를 기반으로 하는 클래스라면 이 모든 클래스에서 생성된 객체를 사용할 때라는 것입니다. , 사용법은 모두 동일합니다.
정규화의 이점은 다음과 같습니다.
#做出一个良好的接口 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 중국어 웹사이트의 기타 관련 기사를 참조하세요!