python(九)——面向对象

异常处理

	try:
				可能出现的异常
	except 具体异常类型:
				对异常的处理
	except 具体异常类型:
				对异常的处理
	finally:
			pass
		
	except 可以有多个,except Exception / except 捕捉所有异常,一般写在最后
	无论异常是否发生,finally中的代码都会执行
#处理迭代器取值越界异常
l = [1,2,3,4]
it = l.__iter__()
try:
    print(it.__next__())
    print(it.__next__())
    print(it.__next__())
    print(it.__next__())
    print(it.__next__())
except StopIteration:
    print('发生了异常',StopIteration)	#发生了异常 <class 'StopIteration'>
else:
    print('没有异常发生时,执行此代码')
finally:
    print('即使发生异常,此代码一样执行')

面向对象

面向对象的程序设计的核心是对象,万物皆对象,需要完成的代码功能,
根据职能的不同,定义为一个个类,类中定义不同的方法。
通过实例化类得到的对象来操作其中的方法,完成具体的工作流程

类的定义

class 类名:
		定义静态属性	属性名 = 值
		定义函数	def 函数名():
				定义动态属性
class Demo:
    def __init__(self,name,age):
        self.name = name
        self.age = age
        print(self.__dict__)#{'name': '张三', 'age': 23} self相当于一个空的字典容器,可以存放属性
        print(id(self))#6933512
d = Demo('张三',23) #实例化对象
print(d.name)   #张三
#demo('张三',23)    首先创建了一个对象,创建了一个self变量,调用__init__方法传入参数,
#将参数保存到self变量中,返回self变量给d
print(id(d))#6933512    self 和 对象的内存地址一致,说明这个对象就是类中的self

#init方法是初始化方法,一个对象创建调用init方法,返回self
#如果没有init方法,实例化后返回的结果为空,因为init方法默认会返回self,没有init就没有self返回
self名可以改,但是默认为self,所有类中函数的第一个参数默认都是self
静态属性在类中定义的变量
动态属性就是类中的方法体中定义的变量
静态属性可以通过类和对象操作
动态属性只能通过对象操作
class Demo:
    a = 1
    def __init__(self,name,age):
        self.name = name
        self.age = age
      
    def run(self,num):
        print(self.name + '跑了'  + str(num) + '米')
d = Demo('张三',23) 
print(Demo.a)#1
#print(Demo.name)报错
print(d.name)  #张三
print(d.a)  #1
d.run(5)
Demo.run(d,6)
'''
对象名
    可以调用静态属性
    调用self的属性
    调用方法
类名
    可以调用静态属性
    不能调用self的属性
    可以调用方法,但是需要self对象和方法参数
'''
d = Demo('张三',23) 
d.__dict__['sex'] = '男'
print(d.__dict__)#{'name': '张三', 'age': 23, 'sex': '男'}
print(d.sex)#通过对象的__dict__属性来修改self的属性,但是不推荐

#修改静态变量
d.a = 3
print(d.a)#3
Demo.a = 2
print(Demo.a)#2

动态创建属性

Python是一门动态语言。通常,动态语言允许我们在程序运行时给对象绑定新的属性或方法,
当然也可以对已经绑定的属性和方法进行解绑定
class Demo:
   # __slots__ = ('__name')
    def __init__(self,name):
        self.__name = name

d = Demo('张三')
d.age = 23	#依赖对象添加动态属性
Demo.age = 23	#依赖类添加静态属性
print(d.age)
#如何禁止对象添加
#__slots__ = ('__name') 限定当前类的对象只能绑定_name属性,对子类不起作用


类的命名空间

class Demo:
    def __init__(self,name,age):
        self.name = name
        self.age = age
d = Demo('张三',23) 
'''
class文件运行会在内存中创建一个命名空间,存入静态属性名,函数名
对象实例化后,为引用d创建另外一个空间,这个空间有一个标识称为类对象指针,指向class的命名空间
对象以此找到这个class空间,但是class空间不能反向找到对应的实例化对象的空间,可以有多个实例化对象空间指向同一个class空间

对象名.属性名 调用属性时,会先去自己的实例化空间中找对应的属性值,找不到就去class的命名空间中找,再去父类中找,找不到抛异常
类名.属性名	调用属性时,会去class的命名空间中找,找不到去父类找,再找不到抛异常
所以类名.属性名 只能调用静态属性,不能调用self中的属性,self中的属性是对象的属性
'''
class Demo:
    a = 1
    def __init__(self,name,age):
        self.name = name
        self.age = age

d1 = Demo('张三',23) 
d2 = Demo('李四',42)
d1.a = 10
print(d1.a) #10
print(d2.a)     #1
print(Demo.a)    #1
print(d1.__dict__)#{'name': '张三', 'age': 23, 'a': 10}
print(d2.__dict__)#{'name': '李四', 'age': 42}
Demo.a = 22

print(d1.a) #10
print(d2.a)     #22
print(Demo.a)    #22
'''
对于静态属性的修改,因为对象和class的内存空间不一致
所以对象.静态属性值修改时,会去自己内存中查找,没有就创建这个变量
因而d1.__dict__可以看到多了一个变量
class空间是所有实例化对象的源头,一旦以class.静态变量名来修改,则所有实例化的对象中没有同名变量的都会被修改
以上仅对于静态变量为不可变数据类型有效
若静态变量为可变数据类型,无论是对象名或者类名去操作静态属性,所有的对象和类中的数据都会发生变化
'''
class Demo:
    l = ['dog']
d1.l[0] = 'car'
print(d1.l) #['car']
print(d2.l)     #['car']
print(Demo.l)    #['car']
print(d1.__dict__)#{'name': '张三', 'age': 23}
print(d2.__dict__)#{'name': '李四', 'age': 42}
Demo.l[0] = 22

print(d1.l) #[22]
print(d2.l)     #[22]
print(Demo.l)    #[22]

#对于不可变数据类型最好使用类名来操作

绑定函数

	凡是类中的方法都是绑定给对象使用的
	绑定方法都可以自动传值,传的是对象本身
class Demo:
    def __init__(self,name,age):
        self.name = name
        self.age = age
    def foo(self):
        pass
d1 = Demo('张三',23) 
print(d1.foo)#绑定方法<bound method Demo.foo of <__main__.Demo object at 0x0000000002083188>>
print(Demo.foo)#<function Demo.foo at 0x0000000002075A68>
#方法调用
d1.foo()#对象名调用的是绑定方法
Demo.foo(d1)#类名调用的是函数,需要手动传入self对象

#类的绑定方法	@classmethod装饰器绑定到方法上就是类的绑定方法	
#当对象在调用类的绑定方法时,也会默认把类当作参数传递进去

#非绑定方法
#@staticmethod,可以解除绑定关系,将一个类中的方法,变为一个普通函数。

面向对象的组合用法

	组合:一个类作为另一个类的属性
#组合
class School:
    def __init__(self,name,address):
        self.name = name
        self.address = address 
class Student:
    def __init__(self,name,age,schoolName,schoolAddress):
        self.name = name
        self.age = age
        self.school = School(schoolName,schoolAddress)

s = Student('张三',23,'武汉大学','武汉')
print(s.school.name)#武汉大学

面向对象的三大特性

	封装
	继承
	多态
	只有是面向对象编程的语言都有的三大特性

继承

'''
继承:一个类获取父类的属性和方法
python中类的继承分为:单继承和多继承
'''
class Parent1:
    p1 = 'p1'
    @staticmethod
    def foo():
        print('p1')
class Parent2:
    p2 = 'p2'
    @staticmethod
    def foo():
        print('p2')
        
class Son(Parent1,Parent2):#多继承
    s = 'son'
    @staticmethod
    def so():
        print('sons')
        
n = Son()
n.foo()#子类调用方法如果自身没有就会去父类查找,如果在第一个父类中找到就不在继续找
n.so()#sons
print(Son.__bases__)#查看继承的父类 (<class '__main__.Parent1'>, <class '__main__.Parent2'>)
print(Son.mro())#查看广度优先的继承顺序 [<class '__main__.Son'>, <class '__main__.Parent1'>, <class '__main__.Parent2'>, <class 'object'>

#派生属性:父类中没有而子类独有的
#派生方法:父类中没有而子类独有的方法
#如果没有指定父类,默认继承Object类,Object类是所有python类的父类,它提供了一些常用的方法实现

子类中执行父类代码

1.父类.方法名
2.super()	常用,python3
class Animals:
    def __init__(self,name,age):
        self.name = name
        self.age = age 
        
class Dog(Animals):
    def __init__(self,name,age,kind):
        super().__init__(name, age)
        self.kind = kind
        
    def toString(self):
        print(self.name + '今年' + self.age + '岁,品种是' + self.kind)
        
d = Dog('大黄','3','中华田园犬')
d.toString()

'''
super()在子类中调用父类,完整写法是super(Dog,self),省略了2个参数
super(子类类名,子类对象名)在子类之外调用父类
'''

继承中经典的钻石问题

'''
钻石问题:
条件:
Parent2和Parent2都继承Parent1
Son继承Parent2和Parent3
流程:
Son调用自身没有的foo方法,程序开始时知道Parent2和Parent3有一个公共父类Parent1,
所以程序先去第一个父类Parent2中找,找到就执行;找不到就去Parent3(后面有机会再次查询公共父类),再找不到就去Parent1找
python2是深度优先
python3是广度优先
广度优先:先找Parent2再找Parent3
深度优先:先找Parent2再找Parent1,最后找Parent3
'''
class Parent1:
    p1 = 'p1'
    @staticmethod
    def foo():
        print('p1')
class Parent2(Parent1):
    p2 = 'p2'
    @staticmethod
    def foo():
        print('p2')
class Parent3(Parent1):
    p2 = 'p3'
    @staticmethod
    def foo():
        print('p3')
        
class Son(Parent2,Parent3):#多继承
    s = 'son'
    @staticmethod
    def so():
        print('sons')
n = Son()
n.foo()
'''
钻石问题2:
条件:
Parent3继承Parent1
Parent4继承Parent2
Son 继承 Parent3和Parent4
流程:
Son调用自身没有的foo方法,程序开始时知道Parent3和Parent4没有公共的父类,
所以程序先去第一个父类Parent3中找,找到就执行;找不到就去Parent3的父类Parent1中找,
再找不到就去Parent4找,找不到就去Parent4的父类Parent2中找
'''

super()本质

super()本质不是直接找父类,而是根据调用者的节点位置的广度优先顺序决定的
class Parent1:
    def foo(self):
        print('p1')
class Parent2(Parent1):
    p2 = 'p2'
    def foo(self):
        super().foo()
        print('p2')
class Parent3(Parent1):
    def foo(self):
        super().foo()
        print('p3')
        
class Son(Parent2,Parent3):#多继承
    def foo(self):
        super().foo()
        print('sons')
n = Son()
n.foo()
#p1
#p3
#p2
#sons
'''
super()先调用Parent2的foo(),
Parent2中的super()调用Parent3的foo()
Parent3中super()调用Parent1
广度优先

注:以上问题只在多继承中出现
'''

接口类与抽象类

接口类:python原生不支持,支持多继承,不能实例化,类中所有方法都不能有具体的实现,子类必须实现其中的抽象方法
抽象类:python原生支持,不支持多继承,不能实例化,类中方法可以有简单的实现,子类必须实现其中的抽象方法

抽象类和接口类是不能够创建对象的类,这种类的存在就是专门为了让其他类去继承它
Python从语法层面并没有像Java或C#那样提供对抽象类的支持,
但是我们可以通过abc模块的ABCMeta元类和abstractmethod包装器来达到抽象类的效果
如果一个类中有抽象方法,这个类就是抽象类,不能被实例化
接口类也是抽象类的一种

java本来就支持单继承 所以就有了抽象类
java编程语言没有多继承 所以为了接口隔离原则,设计了接口这个概念

依赖倒置原则:
高层模块不应该依赖低层模块,二者都应该依赖其抽象;抽象不应该应该依赖细节;
细节应该依赖抽象。换言之,要针对接口编程,而不是针对实现编程

接口隔离原则:
使用多个专门的接口,而不使用单一的总接口。即客户端不应该依赖那些不需要的接口。
#转载https://www.cnblogs.com/Eva-J/articles/7293890.html
#定义接口类
from abc import abstractmethod,ABCMeta
class abstract(metaclass = ABCMeta):#metaclass 定义元类
    @abstractmethod	#定义该方法为抽象方法,子类必须重写,且当前的这个父类不能实例化
    def pay(self,money):
        pass
	#定义一个方法的规范,所有子类必须重写这个方法
'''
class abstract(metaclass = ABCMeta):#metaclass 定义元类
    @abstractmethod	
    def pay(self,money):
       raise NoImplemented	#若子类没有重写该方法又调用了该方法,则抛出指定的NoImplemented异常
'''
class Wechatpay(abstract):
    def pay(self,money):
        print('微信支付了%s元'%money)
        
class Alipay(abstract):
    def pay(self,money):
        print('支付宝支付了%s元'%money)

        
def pay(obj,money):#归一化的入口, 多态性
    obj.pay(money)
    
a = Alipay()
a.pay('100')
w = Wechatpay()
w.pay('150')
#定义抽象类
import abc
class All_file(metaclass=abc.ABCMeta):
    all_type='file'
    @abc.abstractmethod #定义抽象方法,无需实现功能
    def read(self):
        '子类必须定义读功能'
        pass

    @abc.abstractmethod #定义抽象方法,无需实现功能
    def write(self):
        '子类必须定义写功能'
        pass



class Txt(All_file): #子类继承抽象类,但是必须定义read和write方法
    def read(self):
        print('文本数据的读取方法')

    def write(self):
        print('文本数据的读取方法')

class Sata(All_file): #子类继承抽象类,但是必须定义read和write方法
    def read(self):
        print('硬盘数据的读取方法')

    def write(self):
        print('硬盘数据的读取方法')

class Process(All_file): #子类继承抽象类,但是必须定义read和write方法
    def read(self):
        print('进程数据的读取方法')

    def write(self):
        print('进程数据的读取方法')

wenbenwenjian=Txt()

yingpanwenjian=Sata()

jinchengwenjian=Process()

#这样大家都是被归一化了,也就是一切皆文件的思想
wenbenwenjian.read()
yingpanwenjian.write()
jinchengwenjian.read()

print(wenbenwenjian.all_type)
print(yingpanwenjian.all_type)
print(jinchengwenjian.all_type)

多态

多态指的是一类事物有多种形态,(一个抽象类有多个子类,因而多态的概念依赖于继承)
一种调用方式,不同的执行效果(多态性)
子类重写父类方法,同一个方法在不同的子类有不同的实现,调用子类重写的方法,表现出不同的行为就是多态
from abc import abstractmethod,ABCMeta
class Animals(metaclass = ABCMeta):
    def __init__(self,name,age):
        self.name = name
        self.age = age
    @abstractmethod 
    def make(self):
        pass   
class Dog(Animals):
    def __init__(self,name,age,kind):
        super().__init__(name,age)
        self.kind = kind
    def make(self):
        print(self.name + '正在晒太阳')
class Car(Animals):
    def __init__(self,name, age,kind):
        super().__init__(name, age)
        self.kind = kind
    def make(self):
        print(self.name + '正在伸懒腰')

d = Dog('大黄','13','田园犬')
c = Car('汤姆','9','狸花猫')
d.make()
c.make()
'''
多态基于继承关系的,Dog类和Car类都是继承Animals类,在多态的理论中,Dog的对象d和Car的对象c都是Animals类型
可以使用isinstance(d, Animals) True 子类对象也可以看做父类的类型
对于同一个方法,子类调用后表现出不同的行为,这就是多态

鸭子类型:
	多个类有相似的方法,但不是继承自父类,调用任意类的的相似方法都可以得到结果(不检查对象的类型)
	ist和tuple两个就是鸭子类型
	当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就可以被称为鸭子
class Duck():
   def walk(self):
        print('I walk like a duck')
   def swim(self):
        print('i swim like a duck')

class Person():
     def walk(self):
       print('this one walk like a duck') 
    def swim(self):
      print('this man swim like a duck')
当有一个函数调用Duck类,并利用到了两个方法walk()和swim()。
我们传入Person类也一样可以运行,函数并不会检查对象的类型是不是Duck,
只要他拥有walk()和swim()方法,就可以正确的被调用
'''

封装

隐藏一切可以隐藏的实现细节,只向外界暴露(提供)简单的编程接口
隐藏类中的属性和方法,只让自己类的对象去调用
#设置私有属性和私有方法
class Demo:
    __num = 10	#私有静态属性
    def __init__(self,name,age,sex):
        self.name = name
        self.age = age 
        self.__sex = sex	#私有动态属性
    def __private(self):	#私有方法
        print('私有属性' + self.__sex)
        print('私有方法')
    def do_privatr(self):
        self.__private()
d = Demo('张三','23','男')
print(d.name)
#print(d.__num)报错
#print(d.__sex)报错
#d.__private()报错
d.do_privatr()	#调用对象的普通方法,通过普通方法调用自身的私有方法
'''
凡是私有的只允许在函数自身内部调用,self.属性名/方法名
双下划线设置私有的属性,方法

Python并没有从语法上严格保证私有属性或方法的私密性,
它只是给私有的属性和方法换了一个名字来妨碍对它们的访问,
事实上如果你知道更换名字的规则仍然可以访问到它们

__的实质是将__属性名改写为_当前类名__属性名
所以父类的同名私有属性和子类的同名私有属性不是真正意思的同名,子类无法调用父类的私有属性

所有在类外以对象.__属性名无法调用
只能以对象._当前类名__属性名

'''
print(d._Demo__num) #10 在类外以这种方式可以访问私有的变量
#在类中self调用属性/方法会自动添加_类名,类外部不会,在类外部定义的私有属性是无效的,只是一个普通属性

封装应用

class Demo:
    def __init__(self,name):
        self.__name = name
        
    def getName(self):	#获取属性的方法
        return self.__name
    
    def setName(self,name):	#修改属性的方法
        self.__name = name 

d = Demo('张三')
print(d.getName()) #张三
d.setName('李四')
print(d.getName())  #李四

#对类中属性进行封装,隐藏具体的代码,只对外提供修改和获取的接口
#但是python提供属性的获取和修改相关的内置函数是property
'''
内置装饰器函数 property() 只在面向对象中使用,将一个方法伪装成一个属性,以调用属性的方法调用这个方法
同时提供修改和删除的功能
'''

class Demo:
    def __init__(self,name):
        self.__name = name
        
    @property
    def test(self): #获取属性的方法
        print('获取属性的方法运行中')
        return self.__name + 'hello' 
    
    @test.setter
    def test(self,value):   #修改属性的方法
        print('修改属性的方法运行中')
        if not isinstance(value, str):
            print(type(self))
            raise TypeError('%s must be str' %value)
        self.__name = value
    
    @test.deleter
    def test(self): #删除属性的方法
        print('删除属性的方法运行中')
        del self.__name
        
d = Demo('张三')
print(d.test)   #以调用属性的方式调用这个方法
d.test = '李四'   #修改属性
print(d.test)
del d.test  #删除属性
#运行结果
#获取属性的方法运行中
#张三hello
#修改属性的方法运行中
#获取属性的方法运行中
#李四hello
#删除属性的方法运行中

'''
@property装饰的方法参数除了self不能有其它参数,用于获取属性的值
@方法名.setter (方法名为@property装饰的方法名)  , 用于修改属性的值
@方法名.deleter (方法名为@property装饰的方法名)  , 用于删除属性的值
三个装饰器装饰的方法名一致

'''
#property本质就是实现了get,set,delete三种方法
#使用装饰器的方式定义set,get,del方法
class Foo:
    @property
    def AAA(self):
        print('get的时候运行我啊')

    @AAA.setter
    def AAA(self,value):
        print('set的时候运行我啊')

    @AAA.deleter
    def AAA(self):
        print('delete的时候运行我啊')

#只有在属性AAA定义property后才能定义AAA.setter,AAA.deleter
f1=Foo()
f1.AAA
f1.AAA='aaa'
del f1.AAA
'''
def get_AAA(self):
        print('get的时候运行我啊')

    def set_AAA(self,value):
        print('set的时候运行我啊')

    def delete_AAA(self):
        print('delete的时候运行我啊')
    AAA=property(get_AAA,set_AAA,delete_AAA 使用内置函数定义get,set,del方法
'''

类方法

当一个类只涉及静态属性操作时,可以定义为类方法
使用@classmethod装饰,方法参数第一个默认为cls,表示这个类,和self代表对象一样
直接以类名.方法名()调用,不依赖与任何对象
class Demo:
    __num = 100
    def __init__(self,name):
        self.__name = name
        
    @classmethod
    def set(cls,newValue): #只进行静态属性的操作
        cls.__num = newValue
    def show(self):
        return self.__num
d = Demo('张三')
print(d.show() )   #100 
Demo.set(200)   #对于静态属性的操作尽量以类来操作   
print(d.show() )   #200

类方法应用

#查看子类实例化的次数
class Demo(object):
    __num = 0
    def __init__(self,name):
        self.__name = name
        
    @classmethod
    def addNum(cls): #只进行静态属性的操作
        cls.__num += 1
    @classmethod    
    def getNum(cls):
        return cls.__num
    
    def __new__(self):
        Demo.addNum()
        return super(Demo, self).__new__(self)
class Test(Demo):
    def __init__(self):
        print('hh')
t = Test()	#'hh'
t = Test()	#'hh'
t = Test()	#'hh'
print(Demo.getNum())	#3
'''
__new__方法是创建对象实例的方法
__init__是初始化的方法,为对象添加属性

 一个类实例化时,如果没有重写__new__方法,就默认调用父类的__new__方法,父类没有重写就调用object的__new__方法
 创建对象实例后,调用自身的__init__方法完成初始化
 
 def __new__(cls,*args,**kwagrs):
        return super().__new__(cls,*args,**kwagrs)
super()换为自身类会造成死循环
参数cls代表自身类,object会创建自身类的实例
如果将cls换成其他类名,返回的就是其他类的实例化对象


'''

静态方法

静态方法主要是用来存放逻辑性的代码
和类本身没有关系,在静态方法中,通常不会涉及到类中的属性和方法的操作。静态方法是个独立的、单纯的函数,
import time
class Demo(object):
   
    def __init__(self,name):
        self.name = name
        
    @staticmethod
    def get_time():	#获取当前时间
        print(time.strftime('%Y-%m-%d %H:%M:%S',time.localtime(time.time())))
        
Demo.get_time()
发布了16 篇原创文章 · 获赞 0 · 访问量 74

猜你喜欢

转载自blog.csdn.net/qq_31241107/article/details/104011698