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. 小结
*. 编程思想:为子类做规范
归一化设计:几个类都实现了相同的方法
抽象类:最好单继承,且可以简单的实现功能
接口类:最好多继承,且最好不实现具体功能
*. 开放封闭原则:扩展是开放的,修改是封闭的
*. 依赖倒置原则:高层模块不应该依赖低层模块,二者都应该依赖其抽象;抽象不应该应该依赖细节;细节应该依赖抽象。换言之,要针对接口编程,而不是针对实现编程
*. 接口隔离原则:使用多个专门的接口,而不使用单一的总接口。即客户端不应该依赖那些不需要的接口。
浙公网安备 33010602011771号