Python3之面向对象
一、定义:面向对象编程(Object Oriented Programming,OOP,面向对象程序设计)
- 面向过程:根据业务逻辑从上到下写垒代码
- 函数式:将某功能代码封装到函数中,日后便无需重复编写,仅调用函数即可
- 面向对象:对函数进行分类和封装,让开发“更快更好更强...”
1.创建类和对象:面向对象编程是一种编程方式,需要使用 “类” 和 “对象” 来实现,所以,面向对象编程其实就是对 “类” 和 “对象” 的使用。
class 类名: 静态属性1 = '静态属性1' # 公开静态属性 只有是这个类的就有这个属性 __静态私有属性2 = '__静态属性2' # 私有静态属性 只有是这个类的就有这个属性 def __init__(self): # 初始化方法 self是对象,是一个必须传的参数 self.name = 'felix' #公开对象属性 self.__age = 18 #私有对象属性 def func1(self): #类的动态公开方法 一般情况下必须传self参数 pass def __func2(self): #类的动态私有方法 一般情况下必须传self参数 pass 对象 = 类名() #类的实例化 创建对象 对象.静态属性1 #引用类的静态属性1 对象.func1()
说明:
- 关键字:class 使用关键字class 来创建一个类
- 关键字:self 一般情况,必须传的一个参数,代表类本身
- 创建对象:类名称后加括号即可
- 类的公开属性和方法可以在外部调用
- 类的私有属性和方法不能在类的外部调用,只能在类的内部调用
import math class Circle: def __init__(self,r): self.r = r def area(self): return math.pi*(self.r**2) def perimeter(self): return 2*math.pi*self.r A = Circle(5) print(A.area()) print(A.perimeter())
2.静态属性 就是直接在类中定义的变量
(1)类中的静态变量,可以被对象调用
(2)对于不可变数据类型来说,类变量最好用类操作
(3)对于可变数据类型来说,对象名的修改时共享的,重新赋值是独立的
3.动态属性 就是类中定义的方法(函数)
4.命名空间:类和对象分别存在不同的命名空间中
4.组合:一个对象的属性值是另外一个类的对象
import math class Circle: def __init__(self,r): self.r = r def area(self): return math.pi*(self.r**2) def perimeter(self): return 2*math.pi*self.r class Ring: def __init__(self,outside_r,inside_r): self.outside_r = Circle(outside_r) self.inside_r = Circle(inside_r) def area(self): return self.outside_r.area() - self.inside_r.area() def perimeter(self): return self.outside_r.perimeter() + self.inside_r.perimeter() R = Ring(20,10) print(R.area()) print(R.perimeter())
三、三大特性之封装
封装就是在变量的左边加上双下划线,有以下三种情况:
1.类中的静态私有属性
2.类中的私有方法
3.对象的私有属性
所有的私有属性和方法都不能在类的外部调用。
class Person: __key = 123 # 私有静态属性 def __init__(self,name,passwd): self.name = name self.__passwd = passwd # 私有属性 def __get_pwd(self): # 私有方法 return self.__passwd #只要在类的内部使用私有属性,就会自动的带上_类名 def login(self): # 正常的方法调用私有的方法 self.__get_pwd() felix = Person('felix','felix666') print(felix._Person__passwd) # _类名__属性名 print(felix.get_pwd()) #报错,类外部不能调用私有属性 执行结果: felix666 AttributeError: 'Person' object has no attribute 'get_pwd'
四、三大特性之继承
1.定义
继承,面向对象中的继承和现实生活中的继承相同,即:子可以继承父的内容。在python中,新建的类可以继承一个或多个父类,父类又可称为基类或超类,新建的类称为派生类或子类。
2.单继承和多继承
class ParentClass1: #定义父类(基类、超类) pass class ParentClass2: #定义父类(基类、超类) pass class SubClass1(ParentClass1): #Subclass是子类(派生类) 单继承 ,基类是ParentClass1,派生类是SubClass pass class SubClass2(ParentClass1,ParentClass2): #python支持多继承,用逗号分隔开多个继承的类 pass
实例:
class Animal: #父类 基类 超类 def __init__(self): print('执行Animal.__init__') self.func() def eat(self): print('%s eating'%self.name) def drink(self): print('%s drinking'%self.name) def func(self): print('Animal.func') class Dog(Animal): #子类 派生类 def guard(self): #派生方法 print('guarding') def func(self): print('Dog.func') dog = Dog()
- 父类中没有的属性,在子类中出现 叫做派生属性
- 父类中没有的方法,在子类中出现 叫做派生方法
- 只要是子类的对象调用,子类中有名字的,一定用子类的;子类中没有才找父类的,如果父类也没有就报错
- 如果父类、子类都有的属性和方法,就使用子类的;如果还想用父类的(单独调用父类的), 调用方法:父类名.方法名(需要自己传self参数)
调用方法:父类名.方法名(self) #需要传自己的self参数
super().方法名 #不需要自己传self python3
3.继承顺序
那么问题又来了,多继承呢?
- 是否可以继承多个类
- 如果继承的多个类每个类中都定了相同的函数,那么那一个会被使用呢?
class F: def func(self): print('F') class D(F): def func(self): super().func() print('D') class E(F): def func(self): super().func() print('E') class B(D): def func(self): super().func() print('B') class C(E): def func(self): super().func() print('C') class A(B,C): def func(self): super().func() print('A') a = A() a.func() print(A.mro()) 执行结果: F E C D B A [<class '__main__.A'>, <class '__main__.B'>, <class '__main__.D'>, <class '__main__.C'>, <class '__main__.E'>, <class '__main__.F'>, <class 'object'>]
新式类和经典类
关于多继承寻找父类的顺序:新式类广度优先 经典类深度优先
新式类中 有一个类名.mro方法 查看广度优先的继承顺序
在Python3中,有一个super方法,根据广度优先的继承顺序查找上一个类
- Python的类可以继承多个类,Java和C#中则只能继承一个类
- Python的类如果继承了多个类,那么其寻找方法的方式有两种,分别是:深度优先和广度优先(py3)
4.接口类和抽象类
- 接口类和抽象类都是面向对象的规范,所有的接口类和抽象类都不能实例化
- 接口类是支持多继承的,抽象类不支持多继承
(1)接口类
接口类的单继承:
class Wechat(Payment): def pay(self,money): print('已经用微信支付了%s元'%money) class Alipay(Payment): def pay(self,money): print('已经用支付宝支付了%s元' % money) class Applepay(Payment): def pay(self,money): print('已经用applepay支付了%s元' % money) def pay(pay_obj,money): # 统一支付入口 pay_obj.pay(money) # wechat = Wechat() # ali = Alipay() app = Applepay() # wechat.pay(100) # ali.pay(200) p = Payment()
接口类的多继承:
# Tiger 走路 游泳 # Swan 走路 游泳 飞 # Eagle 走路 飞 from abc import abstractmethod,ABCMeta class Swim_Animal(metaclass=ABCMeta): @abstractmethod def swim(self):pass class Walk_Animal(metaclass=ABCMeta): @abstractmethod def walk(self):pass class Fly_Animal(metaclass=ABCMeta): @abstractmethod def fly(self):pass class Tiger(Walk_Animal,Swim_Animal): def walk(self): pass def swim(self): pass class Eagle(Fly_Animal,Walk_Animal): def fly(self): pass def walk(self): pass class Swan(Swim_Animal,Walk_Animal,Fly_Animal): def walk(self): pass def swim(self): pass def fly(self): pass t = Tiger() e = Eagle() s = Swan()
(2)抽象类
import abc #利用abc模块实现抽象类 class All_file(metaclass=abc.ABCMeta): all_type='file' @abc.abstractmethod #定义抽象方法,无需实现功能 def read(self): '子类必须定义读功能' with open('filaname') as f: 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)
小结:
# java :
java里的所有类的继承都是单继承,所以抽象类完美的解决了单继承需求中的规范问题
但对于多继承的需求,由于java本身语法的不支持,所以创建了接口Interface这个概念来解决多继承的规范问题
# python
python中没有接口类概念,但是直接用多继承,实现了接口类 abc模块中的meraclass=ABCMeta @abstructmethod
python中支持抽象类 : 一般情况下 单继承 不能实例化
Python 支持单继承也支持多继承 所以对于接口类和抽象类的区别就那么明显了
五、三大特性之多态
Pyhon不支持Java和C#这一类强类型语言中多态的写法,但是原生多态,其Python崇尚“鸭子类型”。
如果两个类刚好相似,并不产生父类的子类的兄弟关系,而是鸭子类型 即‘如果看起来像、叫声像而且走起路来像鸭子,那么它就是鸭子’
优点 : 松耦合 每个相似的类之间都没有影响
缺点 : 太随意了,只能靠自觉
在面向对象方法中一般是这样表述多态性: 向不同的对象发送同一条消息(!!!obj.func():是调用了obj的方法func,又称为向obj发送了一条消息func),不同的对象在接收时会产生不同的行为(即方法)。 也就是说,每个对象可以用自己的方式去响应共同的消息。所谓消息,就是调用函数,不同的行为就是指不同的实现,即执行不同的函数。 比如:老师.下课铃响了(),学生.下课铃响了(),老师执行的是下班操作,学生执行的是放学操作,虽然二者消息一样,但是执行的效果不同
多态性:
peo=People() dog=Dog() pig=Pig() #peo、dog、pig都是动物,只要是动物肯定有talk方法 #于是我们可以不用考虑它们三者的具体是什么类型,而直接使用 peo.talk() dog.talk() pig.talk() #更进一步,我们可以定义一个统一的接口来使用 def func(obj): obj.talk()
#二者都像鸭子,二者看起来都像文件,因而就可以当文件一样去用 class TxtFile: def read(self): pass def write(self): pass class DiskFile: def read(self): pass def write(self): pass
六、类的装饰器
类的内置函数
1.@property 作用是在新式类中返回属性值。
实例1:
import math class Circle: def __init__(self,r): self.r = r @property def perimeter(self): #用了类的装饰器 property 后,不能在此处加参数 return 2 * math.pi * self.r @property def area(self): #用了类的装饰器 property 后,不能在此处加参数 return math.pi * (self.r ** 2) c1 = Circle(5) print(c1.area) #用了 类的property装饰器后 area 变成了 c1的属性了 print(c1.perimeter) #用了 类的property装饰器后 perimeter 变成了 c1的属性了
实例2:
class Person: def __init__(self,name): self.__name = name @property def name(self): return self.__name + '牛牛牛' @name.setter def name(self,new_name): self.__name = new_name felix = Person('felix') print(felix.name) felix.name = 'all' print(felix.name)
2.@classmethod 修饰符对应的函数不需要实例化,不需要 self 参数,但第一个参数需要是表示自身类的 cls 参数,可以来调用类的属性,类的方法,实例化对象等。
实例1:
class A(object): bar = 1 def func1(self): print ('foo') @classmethod def func2(cls): print ('func2') print (cls.bar) cls().func1() # 调用 foo 方法 A.func2() # 不需要实例化 输出结果为: func2 1 foo
实例2:
class A(object): # 属性默认为类属性(可以给直接被类本身调用) num = "类属性" # 实例化方法(必须实例化类之后才能被调用) def func1(self): # self : 表示实例化类后的地址id print("func1") print(self) # 类方法(不需要实例化类就可以被类本身调用) @classmethod def func2(cls): # cls : 表示没用被实例化的类本身 print("func2") print(cls) print(cls.num) cls().func1() # 不传递传递默认self参数的方法(该方法也是可以直接被类调用的,但是这样做不标准) def func3(): print("func3") print(A.num) # 属性是可以直接用类本身调用的 # A.func1() 这样调用是会报错:因为func1()调用时需要默认传递实例化类后的地址id参数,如果不实例化类是无法调用的 A.func2() A.func3()
实例3:
class Goods: __discount = 0.8 def __init__(self,name,price): self.name = name self.__price = price @property def price(self): return self.__price * Goods.__discount @classmethod #把一个方法 变成一个类中的方法 这个方法就直接可以被类调用,不需要任何依赖 def change_discount(cls,new_diccount): #修改折扣 cls.__discount = new_diccount apple = Goods('苹果',5) print(apple.price) apple.change_discount(0.5) print(apple.price) 执行结果: 4.0 2.5
3.@staticmethod 返回函数的静态方法。
该方法不强制要求传递参数,如下声明一个静态方法:
class C(object): @staticmethod def f(arg1, arg2, ...): ...
以上实例声明了静态方法 f,类可以不用实例化就可以调用该方法 C.f(),当然也可以实例化后调用 C().f()。
class C(object): @staticmethod def f(): print('runoob'); C.f(); # 静态方法无需实例化 cobj = C() cobj.f() # 也可以实例化后调用
class Login: def __init__(self,name,password): self.name = name self.pwd = password def login(self):pass @staticmethod def get_usr_pwd(): # 静态方法 此处没有参数 usr = input('用户名 :') pwd = input('密码 :') Login(usr,pwd) Login.get_usr_pwd() 在完全面向对象的程序中,如果一个函数即和对象没有关系 也和类也没有关系,那么就用staticmethod将这个函数变成一个静态方法
七、反射
python面向对象中的反射:通过字符串的形式操作对象相关的属性。python中的一切事物都是对象(都可以使用反射),是用字符串类型的名字去操作变量。
反射使用的内置函数: hasattr、 getattr、 setattr、 delattr
hasattr和getattr成对出现
class A: def fun(self): print('in func') a = A() a.name = 'felix' a.age = 30 1.反射对象的属性 getattr ret = getattr(a,'name') #通过变量的字符串形式取到的值 print(ret) print(a.__dict__) 变量名 = input('>>>') #func print(getattr(a,变量名)) print(a.__dict__[变量名]) 2.反射对象的方法 getattr ret = getattr(a,'func') ret() 3.反射类的属性 getattr
class A: price = 20 @classmethod def fun(self): print('in func') print(getattr(A,'price')) 4.反射类的方法 getattr # A.fun() if hasattr(A,'fun'): getattr(A,'fun')()
# 模块 内置模块也能用反射 import time print(time.strftime('%Y-%m-%d %H:%M:%S')) print(getattr(time,'strftime')('%Y-%m-%d %H:%M:%S')) import mokuai # 反射模块的属性 # 普通方法 print(mokuai.day) # getattr方法 print(getattr(mokuai,'day')) #反射模块的方法 print(getattr(mokuai,'func')()) import sys def func(): print('反射自己模块的变量') name = 'felix' print(sys.modules) #所有引用的模块 print(sys.modules['__main__']) #当前模块 print(sys.modules['__main__'].name) # #当前模块的变量 name # 反射自己模块中的变量 print(getattr(sys.modules['__main__'],'name')) # 反射自己模块中的方法(函数) getattr(sys.modules['__main__'],'func')() getattr(sys.modules[__name__],'func')() # x = input('>>>') # print(getattr(sys.modules['__main__'],x))
# setattr() 设置一个变量 class A: pass a = A() setattr(A,'name','felix') #设置类的属性 setattr(a,'name','alina') #设置对象的属性 print(A.name) print(a.name) felix
alina # delattr() 删除一个变量 delattr(a,'name') #删除对象的属性 a就去找类的属性 为 felix print(a.name) felix delattr(A,'name') print(a.name) 报错: AttributeError: 'A' object has no attribute 'name'
# setattr() 设置一个变量 class A: pass a = A() setattr(A,'name','felix') #设置类的属性 setattr(a,'name','alina') #设置对象的属性 print(A.name) print(a.name) felix alina # delattr() 删除一个变量 delattr(a,'name') #删除对象的属性 a就去找类的属性 为 felix print(a.name) felix delattr(A,'name') print(a.name) 报错: AttributeError: 'A' object has no attribute 'name'
八、类的内置方法(专有方法)
1.__str__ --->>> str(obj)
2.__repr__ --->>> repr(obj)
class Teacher: def __init__(self, name, salary): self.name = name self.salary = salary def __str__(self): return "Teacher's object :%s" % self.name def __repr__(self): return str(self.__dict__) def func(self): return 'wahaha' felix = Teacher('felix', 8888) print(felix) # 打印一个对象的时候,就是调用a.__str__ print(str(felix)) print(repr(felix)) print('>>> %r' % felix) print(felix.__str__) 执行结果: Teacher's object :felix Teacher's object :felix {'name': 'felix', 'salary': 8888} >>> {'name': 'felix', 'salary': 8888} <bound method Teacher.__str__ of {'name': 'felix', 'salary': 8888}>
print(felix.__str__)
object 里有一个__str__,一旦被调用,就返回调用这个方法的对象的内存地址
%s str() 直接打印 实际上都是走的__str__
%r repr() 实际上都是走的__repr__
repr 是str的备胎,但str不能做repr的备胎
小结:
1.print(obj) / '%s'%obj / str(obj)的时候,实际上是内部调用了obj.__str__方法,如果str方法有,那么他返回的必定是一个字符串
如果没有__str__方法,会先找本类中的__repr__方法,再没有再找父类中的__str__。
2.repr(),只会找__repr__,如果没有就找父类的
3.__len__ --->>> len()
class Classes: def __init__(self,name): self.name = name self.student = [] def __len__(self): return len(self.student) python = Classes('Python3') python.student.append('Felix') python.student.append('Alina') print(len(python)) 执行结果: 2
4.__del__ --->>> 析构函数
class A: def __del__(self): # 析构函数: 在删除一个对象之前进行一些收尾工作 self.f.close() a = A() a.f = open('文件名') # 打开文件 第一 在操作系统中打开了一个文件 拿到了文件操作符存在了内存中 del a # a.f 拿到了文件操作符消失在了内存中 del a # del 既执行了这个方法,又删除了变量
5.__call__ --->>> obj(obj)
class A: def __init__(self, name, age): self.name = name self.age = age def __call__(self): for k in self.__dict__: print(k, self.__dict__[k]) a = A('felix', 18)() 执行结果: name felix age 18
6.item
1.__getitem__
2.__setitem__
3.__delitem__
实例:
class Teacher: def __init__(self,name,age): self.name = name self.age = age def __getitem__(self, item): return self.__dict__[item] def __setitem__(self, key, value): self.__dict__[key] = value def __delitem__(self, key): # del self.__dict__[key] self.__dict__.pop(key) def __delattr__(self, item): # self.__dict__.pop(item) felix = Teacher('felix',18) print(felix.__dict__['name']) felix.__dict__['sex'] = 'boy' print(felix.sex,felix.__dict__['sex']) print(felix.__dict__) # del felix.age #object里的方法 原生支持 执行__delattr__方法 没有写__delattr__也不会报错 # 所以当没有__delitem__方法也不会报错 继承object del felix['age'] #当没有实现__delitem__方法会报错 # print(felix.__dict__['age']) #会报错 print(felix.__dict__) 执行结果: felix boy boy {'name': 'felix', 'age': 18, 'sex': 'boy'} {'name': 'felix', 'sex': 'boy'}