Python之面向对象基础
1.面向对象编程与面向过程编程
面向对象编程,其本质是以建立模型体现出来的抽象思维过程和面向对象的方法。通过把真实的物体抽象成对象,然后赋予对象属性、方法等特性,然后去解决问题。
面向过程编程,本质是面向结果编程,通过函数解决问题。
2. 面向对象基础
a. 类:具有相同竖向和方法的一类事物
b. 对象:具有具体属性的事物
c. 静态属性:所有的对象共有的属性,可以直接调用
d. 对象属性/方法:是属于某一个对象的,只能在实例化之后对象调用
class 类名: # 类名首字母大写 属性 = "功能" # 类的属性:静态属性,不是必须要定义的,当多有的对象都用的时候去定义 def __init__(self,参数): # self是不需要传的 #__init__这里是定义了一个该类对象共有的属性 # 这里的属性是对象属性, self.key = 参数 # 这里self.key就相当于字典的key,参数就相当于value def 方法(self): # 类里面的方法本质上就是函数--函数必须要传一个参数self pass print(类名.属性) # 实例化一个对象,对象就是实例 对象 = 类名("参数") # 参数是传到__init__函数里 对象.函数名() # 调用成功,传入的self就是对象,当用一个对象去调用对他的方法的时候,自动传入的一个参数,这个参数就是对象本身
示例1:计算长方形的面积
class Rectangle: role = "retangle" def __init__(self,long,wide): self.long = long self.wide = wide def length(self): return 2*(self.long+self.wide) def area(self): return self.long*self.wide rect = Rectangle(3,4) ret_l = rect.length() print(ret_l) ret_a = rect.area() print(ret_a)
示例2:计算圆的面积
from math import pi class Circle: rloe = "circle" def __init__(self,r): self.r = r def length(self): return 2*pi*self.r def area(self): return pi*self.r*self.r val = Circle(10) val_l = val.length() print(val_l) val_s = val.area() print(val_s)
3. 命名空间
a. 类的命名空间和对象的命名空间是分开的,且每个对象都有独立的命名空间,公用一个类的命名空间
b. 调用静态属性:调用的就是类中的属性
c. 调用动态属性:对象和类去调用的时候,表现出来的地址是不一样的,但实际是一样的
d. 调用类的静态属性:类调用的就是类中的属性,对象调用先从自己内存空间里找名字,找到了就用自己的,没有找到就用类的,类中也没有的话就报错
e. 调用类的动态属性:这个方法本身就存在类中,并不会存在对象的内存中,但是在对象调用类中的方法的时候,要依赖于一个地址薄去类中寻找对应的方法
f. 类的外面可以添加静态属性或对象属性,动态属性(方法)不可以添加
g. 关于对象的属性:
*. 对象的属性就存在类的命名空间中
*. 只能被对象调用修改
*. 不能被类调用
class Birthday: # 定义一个生日类, role = "birthday" # 静态属性 def __init__(self,year,month,day): # 定义一些生日共有的属性 self.year = year # 对象属性 self.month = month self.day = day class Course: # 定义一个课程类 role = "course" # 静态属性 def __init__(self,name,period,teacher,price): self.name = name # 对象属性 self.period = period self.teacher = teacher self.price = price class Person: # 定义一个人类 role = "person" # 静态属性 def __init__(self,name,haijiao_birth): self.name = name # 静态属性 self.birth = haijiao_birth python = Course("python",5,"Teacher Li",200000) # 实例化python到Course类中 john_birth = Birthday(1999,2,22) # 实例化john_birth到Birthday类中 john = Person("john",john_birth) # 实例化john到Person类中 print(john.birth.month) # john.birth等价于Person类中的self.birth,而self.birth 等于 john_birth,所以就等价于john_birth.month print(john_birth.month) # 从类的外部去调用类内的对象属性 john_class = python # 在外部添加对象属性,在john的小空间里 print(john_class.name) print(john_birth.day)
4. 组合
组合:一个类的对象是另一个类的属性,增强了代码的重用性
from math import pi class Circle: def __init__(self,r): self.r = r def perimeter(self): return 2*pi*self.r def area(self): return pi*self.r*self.r class Ring: def __init__(self,r_out,r_in): self.r_out = Circle(r_out) # 组合 self.r_in = Circle(r_in) def perimeter(self): return self.r_out.perimeter() + self.r_in.perimeter() def area(self): return self.r_out.area() - self.r_in.area() val = Ring(10,5) perimter = val.perimeter() print(perimter) area = val.area() print(area)
5. 面向对象特性之封装
a. 封装:将同一类方法属性封装到类中,通过构造方法,将数据封装到对象中
b. 私有属性,私有方法(双下划线+属性名/方法名)
class Teacher: __identifier = "Teacher" # 私有静态属性 def __init__(self,name,pwd): self.name = name self.__pwd = pwd # 私有属性 self.__p() # 调用私有方法 def pwd(self): print(self.__pwd) def __p(self): # 私有方法 print(self.__pwd) wu = Teacher("Wu","3714") wu.pwd() print(wu.__dict__) # 打印属于wu的属性 # 类外调用私有属性(_类名__私有属性名)--不要去使用这个方法 print(wu._Teacher__pwd) # 类外调用私有方法 wu._Teacher__p() # 类外调用私有静态属性 print(Teacher._Teacher__identifier) # 类外定义私有属性形式的属性,没有任何意义,就跟普通属性一样的
c. 私有方法的意义:
*. 有一些方法的返回值只是用来作为中间结果
*. 父类的方法不希望子类继承
class Teacher: __identifier = "Teacher" def __init__(self,name,pwd): self.name = name self.__pwd = pwd self.__p() def __p(self): return hash(self.__pwd) def judge(self,password): return hash(password) == self.__p() wu = Teacher("wu","3714") ret = wu.judge("3417") print(ret)
d. 私有属性
class Person: def __init__(self,name,height,weight): self.name = name self.__height = height self.__weight = weight def get_bmi(self): return self.__weight /(self.__height * self.__height) def change_weigth(self,new_weight): if new_weight > 20: self.__weight = new_weight else: print("体重过轻") ha = Person("ha",1.81,94) print(ha.get_bmi()) ha.change_weigth(80) print(ha.get_bmi())
示例
class Host: def __init__(self,owner,length,width): self.owner = owner self.__length = length self.__width = width def area(self): return self.__length * self.__width host = Host("lu",50,30) print(host.area())
e. property方法:把类中的方法假装成一个属性
class Person: def __init__(self,name,height,weight): self.name = name self.__height = height self.__weight = weight @property def bmi(self): return self.__weight /(self.__height * self.__height) lu= Person("lu",1.81,94) print(lu.name,lu.bmi) # 这里去调用的时候,就是相当于调用一个属性
*. @要伪装的方法名.setter,这样就更加像属性
class Shop: discount = 0.75 def __init__(self,name,price): self.name = name self.__price = price @property def price(self): return self.__price * Shop.discount @price.setter def price(self,new_price): self.__price = new_price @price.deleter # 一般不会用到 def price(self,): del self.__price apple = Shop("apple",5) print(apple.price) apple.price = 6 # @price.setter,用了这个方法之后,就可以修改了 print(apple.price) print(apple.__dict__) del apple.price print(apple.__dict__)
f. staticmethod:静态方法,函数不需要依赖对象的属性和类的属性,就可以使用staticmethod装饰
class A: def __init__(self,name): self.name = name def ah(self): # self就是形式参数,ah就是普通方法,绑定(对象)方法 print("ah") @staticmethod # 静态方法 def func(): print("func") # a = A("a") # A.func(a) #和a.func()一样 A.func()
g. 类方法:对应的函数不需要实例化,不需要 self 参数,但第一个参数需要是表示自身类的 cls 参数,可以来调用类的属性,类的方法,实例化对象等。
class A: role = "a" @classmethod def class_metood(cls): #这里必须叫cls,默认的 print(cls.role) A.class_metood() # A.role
h. 小结
*. 静态方法--没有必须传的参数,不需要用对象的属性和类的属性
*. 类方法--必须传一个类,不能使用对象属性,但可以使用类属性
*. 普通方法--必须传一个参数,可以使用对象的属性和类的属性
6. 面向对象特性之继承
a. 继承:当两个类有相同的方法或属性的时候,把这些相同的方法属性封装到基类类中,只实现一次,其它类去继承
b. 继承是A类是B类子类的关系,而组合是A类中有B类的关系
*. 父类/超类/基类,子类/派生类
*. object是所有类的基类,type是所有类的类型,metaclass可以指定类的类型
*. 对象可以调用自己本类和父类所有的方法和属性,先调用自己的.
*. 先抽象在继承
抽象---从小类到大类
继承---从大类到小类
c. 单继承,多继承
class Animal: # 父类 def eat(self): pass def drink(self): pass def sleep(self): pass class Bnimal: # 父类 def eat(self): pass def drink(self): pass def sleep(self): pass class Dog(Animal): # 单继承 pass class Cat(Animal,Bnimal): # 多继承 pass print(Dog.__bases__) # (查看所有父类) print(Cat.__base__) # (查看从左到右的第一个父类)
d. super()方法
*. 在继承中,子类可以继承父类所有的属性和方法,当父类和子类中有同名方法的时候,一定调用子类的。如果想使用父类的该方法的功能,需要借助super方法。
class A: def hahaha(self): print("A") class C: def hahaha(self): print("C") class B(A,C): def hahaha(self): # B.hahaha(self) # 经典类的调用父类中方法的方式 # super--新式类,当多个父类有同名方法时,先从前面找,优先使用第一个找到的 # super(B,self).hahaha() # 里面这里可以传,或者不传, super().hahaha() # 一般不传,因为在子类里边,很容易就知道是哪个类 print("B") b = B() b.hahaha() # 调用了自己的hahaha() super(B,b).hahaha() # 相当于A.hahaha(),外部的时候,必须传参
示例
class Animal: #---父类 def __init__(self,name): self.name = name def eat(self): print("%s is eating %s"%(self.name,self.food)) def drink(self): print("%s is drinking %s" % (self.name, self.food)) def sleep(self): print("%s is sleeping %s" % (self.name)) class Dog(Animal):#---单继承 def __init__(self,name): self.food = "狗粮" super().__init__(name) def say(self): print("汪汪汪") class Cat(Animal): def __init__(self,name): self.food = "猫粮" super().__init__(name) def say(self): print("喵喵喵") wang = Dog("wang") wang.eat()A c = Cat("egon") c.eat()
class Animal: def __init__(self,name,food): self.name = name self.food = food self.family = "大森林" def eat(self): print("%s is eating %s"%(self.name,self.food)) def drink(self): print("%s is drinking"%(self.name)) def sleep(self): print("%s is sleeping"%(self.name)) def call(self): print("%s is calling %s" % (self.name,self.calling)) class Dog(Animal):#游泳 def __init__(self,name): super().__init__(name, "骨头") self.calling = "汪汪汪" self.family = "小破屋" def swim(self): print("%s swimming"%self.name) class Bird(Animal): #飞 def __init__(self,name): super().__init__(name,"虫子") self.calling = "布谷" def fly(self): print("%s flying"%self.name) erha = Dog("erha") erha.call() #调用父类的方法 erha.swim() #调用自己的方法 print(erha.family) #优先使用自己的 print("******************************") cuckoo = Bird("cuckoo") cuckoo.call() print(cuckoo.family)#因为他本身没有,所以直接用父类的
e. 派生属性和派生方法
*. 派生属性--在父类没有的基础上,子类自己有的属性
*. 派生方法--在父类没有的基础上,子类自己的方法
class Animal: #父类,人和动物都有的一些属性 def __init__(self,name,blood,aggr): self.name = name self.blood = blood self.aggr = aggr class Person(Animal): #继承的语法,Person是子类,继承了父类Animal的属性 def __init__(self,name,blood,aggr,money): super(Person,self).__init__(name,blood,aggr) #在子类执行父类的方法 self.money = money #派生属性 def attack(self,dog): #派生方法 dog.blood -= self.aggr class Dog(Animal): #继承的语法,Dog是子类,继承了父类Animal的属性 def __init__(self,name,blood,aggr,breed): super().__init__(name,blood,aggr) self.breed = breed def attack(self, person): person.blood -= self.aggr alex = Person("alex",2000,250,1000000) erha = Dog("erha",2000,250,"金毛")
f. 经典类,新式类
*. 经典类和新式类的区别:
1. 定义顺序
2. 子类执行父类中的同名方法
3. 继承问题
*. python2里有经典类和新式类之分
经典类--深度优先
新式类--广度优先
class A: # 经典类 pass class B(object): # 新式类 pass
*. 在python3中,object是所有类的基类
g. 钻石继承:python的新式类和经典类在继承顺序上的不同
class F: def test(self): print("F") class E(F): pass def test(self): print("E") class D(F): pass def test(self): print("D") class C(E): pass def test(self): print("C") class B(D): pass def test(self): print("B") class A(B,C): pass def test(self): print("A") a = A() a.test() A-B-D-C-E-F
class D(): pass def test(self): print("D") class C(): pass def test(self): print("C") class B(D): pass def test(self): print("B") class A(B,C): pass def test(self): print("A") a = A() a.test() # 这个的继承是A-B/C,B-D,C没有父类 # 这种情况下,A中没有时,先找B,B中没有时先找D,D里边没有的时候,就找C
7. 多态
a. 多态指的是一类事物有多种形态,python天生支持多态,不同的类可以有一样的方法,但是功能是不一样。
b. 鸭子模型
对于一个对象来说,只要它能鸭子叫,就是鸭子,不管它是什么形态,这就是多态
对于一个对象来说,只要能使用一个方法,就不管他是什么形态的
8. 抽象类和接口类
a. 接口类:归一化设计
from abc import abstractmethod,ABCMeta
class Payment(metaclass=ABCMeta): # 接口类/抽象类,子类的规范(约束子类必须要有这个里的方法,不然就抛异常) @abstractmethod # 加了一个装饰器 def payment(self,money): raise NotImplemented class Applepay(Payment): def payment(self,money): print("apple 支付了 %d元"%money) class Alipay(Payment): def payment(self,money): print("支付宝 支付了 %d元"%money) class Wechatpay(Payment): def huaqian(self,money): print("微信 支付了 %d元"%money) def payment(pay_obj,money): pay_obj.payment(money) apple = Applepay() ali = Alipay() payment(apple,200) payment(ali,100) wechat = Wechatpay() wechat.payment(wechat,100) # 因为他没有payment方法,所以会报错 # TypeError: Can't instantiate abstract class Wechatpay with abstract methods payment
b. 抽象类:抽象类是一个特殊的类,它的特殊之处在于只能被继承,不能被实例化。如果说类是从一堆对象中抽取相同的内容而来的,那么抽象类就是从一堆类中抽取相同的内容而来的,内容包括数据属性和函数属性。
from abc import abstractmethod,ABCMeta
class Foo(metaclass=ABCMeta): # 抽象类 @abstractmethod def read(self): # 抽象类去实现了方法 f = open() f.read() f.close() def write(self): f = open() f.write() f.close() class File(Foo): def read(self): super().read() def write(self): super().write() class Disk(Foo): def read(self): super().read() def write(self): super().write()
c. 对比
a. 接口类不实现具体的方法,并且可以多继承;抽象类可以做一些基础实现,并且不推荐多继承
b. 在抽象类中,我们可以对一些抽象方法做出基础实现;而在接口类中,任何方法都只是一种规范,具体的功能需要子类实现
d. 小结
*. 编程思想:为子类做规范
归一化设计:几个类都实现了相同的方法
抽象类:最好单继承,且可以简单的实现功能
接口类:最好多继承,且最好不实现具体功能
*. 开放封闭原则:扩展是开放的,修改是封闭的
*. 依赖倒置原则:高层模块不应该依赖低层模块,二者都应该依赖其抽象;抽象不应该应该依赖细节;细节应该依赖抽象。换言之,要针对接口编程,而不是针对实现编程
*. 接口隔离原则:使用多个专门的接口,而不使用单一的总接口。即客户端不应该依赖那些不需要的接口。