Python开发——9.面向对象编程

一、面向对象设计(Object oriented design)

  面向对象设计是将一类具体事物的数据和动作整合到一起的过程,不会要求面向对象语言编程,但如果想要构造具备对象性质和特点的数据类型,需要更多考虑程序

def dog(name,gender,type):
    def call_dout(dog):
        print("一条狗[%s],汪汪叫"%name)
    def eat_meat(dog):
        print("一只[%s],正在吃肉" %type)
    def init(name,gender,type):
        dog1={
            "name":name,
            "gender":gender,
            "type":type,
            "jiao":call_dout,
            "chirou":eat_meat,
        }
        return dog1
    return init(name,gender,type)
d1=dog("dagouxiong","gong","zangao")
print(d1)
d1["call_out"](d1)
d1["eat_meat"](d1)

 

二、类和对象

1.类

  类是一个抽象的概念,是一种数据结构,用来描述一类事物(事物即数据和动作的结合体),用它来生产真实的物体(实例)

2.对象

  可以把对象理解为一个具体的事物(事物即数据和动作的集合体)

3.类和对象

  对象都是由类产生的,可以把类看做一个模板,对象都是由类这个模板产生的

4.实例化

  由类产生对象的过程叫做实例化,实例化产生的结果就是一个对象,或者叫做实例

三、类

1.声明类

  声明类的过程与声明函数的过程很相似

'''
class 类名:
    "类的文档字符串"
    类体
'''
class Data:#首字母大写
    pass
d1 = Data()#由类Data实例化产生一个对象d1

 

2.新式类和经典类

(1)只有在Python2中才区分新式类和经典类,Python3中都是新式类

(2)区别:新式类必须继承至少一个父类

(3)所有类都会有一个默认继承的object父类

(4)Python2中,经典类和新式类不同的表现形式

#经典类
class Data:
    pass
#新式类
class Data(Father):
    pass

  Python3中以上两种形式都是新式类

3.属性

  属性分为数据属性和函数属性,类和对象都是通过点的方式来访问自己的属性

(1)数据属性:即变量

(2)函数属性:即函数,在面向对象里又称为方法

4.类的数据属性

  类的数据属性又称为静态变量或静态数据,这些数据是和所属类绑定的,不依赖任何实例

class Chinese:
    country = "China"#数据属性
    def __init__(self,name):
        self.name  = name
    def play_ball(self,ball):
        print("%s正在打%s"%(self.name))

5.类的函数属性(又称为方法)

  命名规则(动词+名词)

class Chinese:
    country = "China"def play_ball(self,ball):#函数属性
        print("正在打球")

6.查看类属性

  两种方式来查看类的属性:(1)dir(类名),结果是一个名字列表;(2)类名.__dict__:结果是一个字典,key为属性名,value为属性值

class American:
    "这是一个美国人的类"
    sport = "basketball"
    def __init__(self,name,age,gender):
        self.name=name
        self.age=age
        self.gender=gender
    def play_basketball(self):
        print("投进了一个三分球")
print(American.__dict__)
print(dir(American))
#特殊的类属性
print(American.__name__)#类的名字:American
print(American.__doc__)#类的文档字符串:这是一个美国人的类
print(American.__base__)#类的第一个父类:<class 'object'>
print(American.__bases__)#类的所有父类构成的元组:(<class 'object'>,)
print(American.__module__)#类定义所在的模块:__main__
print(American.__class__)#实例对应的类:<class 'type'>

四、对象

1.实例化

  对象是由类实例化而来,实例化的结果称为一个对象或实例,定制实例的数据属性可以用类的一个内置方法__init__(),该方法在类实例化时会自动执行

class American:
    "这是一个美国人的类"
    sport = "basketball"
    def __init__(self,name,age,gender):#实例化的过程可以简单理解我执行该函数的过程,实例本身会当做参数传递给self(这是默认的步骤)
        self.name=name
        self.age=age
        self.gender=gender
    def play_basketball(self):
        print("投进了一个三分球")
a1 = American("Kobe",38,"male")

2.查看实例属性

  与查看类属性相同,dir(实例名) 和实例名.__dict__  

class American:
    "这是一个美国人的类"
    sport = "basketball"
    def __init__(self,name,age,gender):
        self.name=name
        self.age=age
        self.gender=gender
    def play_basketball(self):
        print("投进了一个三分球")
a1 = American("Kobe",38,"male")
print(a1.__dict__)
print(dir(a1))

  注意:不要修改__dict__返回的字典

3.类属性和实例属性的增删改查

(1)类属性的增删改查

class Chinese:
    country = "China"
    def __init__(self,name):
        self.name  = name
    def play_ball(self,ball):
        print("正在打球")
print(Chinese.country)#
Chinese.country="中国"#
Chinese.location="Asia"#
del Chinese.country#

(2)实例属性的增删改查

class Chinese:
    country = "China"
    def __init__(self,name):
        self.name  = name
    def play_ball(self,ball):
        print("%s正在打球"%(self.name,ball))
p1 = Chinese("dazui")
print(p1.__dict__)

#查看
print(p1.name)
print(p1.play_ball)

#增加
p1.age=19
print(p1.__dict__)
print(p1.age)

#修改
p1.age=20
print(p1.age)

#删除
del p1.age
print(p1.__dict__)

4.作用域

class MyData:
    pass
x = 50
y = 100
MyData.x=5
MyData.y=10
print(x,y)
print(MyData.x,MyData.y)
print(MyData.x+MyData.y)

  与函数类似,类也有作用域的概念,class可以看做最外层的函数,是一个作用域。实例化会自动触发__init__函数的运行,返回一个实例,要找到实例属性就保存在__init__函数的局部作用域里。类和实例的字典属性即他们各自的作用域。因此一个点就代表一层作用域,实例先从自己的作用域找,自己找不到去外层的类的字典中找,都找不到就会报错。在类中,使用没有点的调用就是调用全局变量

五、静态属性、类方法、静态方法

1.静态属性

  静态属性:通过@property装饰器把函数属性封装成数据属性的形式,调用时外部看不到,无法修改

class Room:
    def __init__(self,name,owner,length,width,heigh):
        self.Name = name
        self.Owner = owner
        self.Length = length
        self.Width = width
        self.Heigh = heigh
    @property#封装逻辑
    def cal_area(self):
        return self.Length*self.Width

r1 = Room("303","dazui",10,8,3)
print(r1.cal_area)
# r1.cal_area = 10#会报错

2.类方法

  类方法:通过@classmethod装饰器实现,类方法只能访问类的数据属性,不能访问实例的数据属性

class Dog:
    name = "Jack"
    
    def __init__(self, name):
        self.name = name
        
    @classmethod
    def talk(self):
        print("%s is talking." % self.name)
dog = Dog("Tom")  # 实例化一只叫Tom的狗
dog.talk()#Jack is talking.

3.静态方法

  静态方法:通过@staticmethod装饰器实现,静态方法不可以访问实例变量或类变量,唯一与类有关联的是需要通过类名来调用这个方法

class Dog:
    name = "Jack"
    def __init__(self, name):
        self.name = name

    @staticmethod  # 属于类的一种方法,但无法访问类或实例的属性
    def eat(self, food):
        print("%s is eating %s" % (self.name, food))
    @staticmethod
    def drink(name,water):
        print("%s is eating %s" % (name, water))

dog = Dog("Tom")# 当eat变成静态方法后,再通过实例调用时就不会自动把实例本身当作一个参数传给self
# dog.eat("Apple")# TypeError: eat() missing 1 required positional argument: 'food'
dog.eat(dog, "Apple")# 正确的姿势
dog.drink("Jack","Coco")# 当作普通方法使用,传入位置参数
Dog.drink("Jerry", "Sprit")# 直接通过类名调用

六、组合

  组合是一个类中使用到另一个类,从而把几个类拼到一起,组合的作用是做关联并减少重复代码

#定义一个房间
class Door:
    pass
class Window:
    pass
class Wall:
    pass

class Room:
    def __init__(self,name,door,window,wall):
        self.name = name
        self.door = Door()
        self.window = Window()
        self.wall = Wall()

七、面向对象编程三大特性之继承

1.类的继承

  继承是一种创造新类的方式,在Python中新建的类可以继承自一个或多个父类,继承一个父类称为单继承,继承多个父类称为多继承。父类又称为基类,子类又称为派生类。

  子类继承了父类的所有属性,但即使子类定义的名字与父类相同,子类也不会覆盖父类

class Parentclass1:
    pass
class Parentclass2:
    pass
class Subclass1(Parentclass1):#单继承
    pass
class Subclass2(Parentclass1,Parentclass2):#多继承
    pass

2.组合和类的用法区别

  当类之间有显著不同,且较小的类可以组成较大的类时,用组合较好;当类之间有很多相同的功能,提取这些共同的功能作为基类,用继承

  使用继承的话,任何一点小的变化也需要重新定义一个类,很容易引起类的爆炸式增长,产生一大堆有着细微不同的子类. 所以有个“多用组合少用继承”的原则。

3.派生、继承和继承结构

  派生就是子类在继承父类的基础上衍生出新的属性。子类中独有的,父类中没有的;或子类定义与父类重名的东西。子类也叫派生类。

  继承是一种子类属性从父类继承的一种方式

  继承结构表示多“代”派生,连续的子类都与祖先类有关系

4.接口继承

  继承同时具有两种含义:(1)继承基类的方法,并且做出自己的改变或扩展;(2)声明某个子类兼容于某基类,定义一个接口类,子类继承接口类,并且实现接口中定义的方法

  继承的第一种含义意义并不大,并且会使得子类与基类出现强耦合,第二种含义又叫“接口继承”,它很重要。

  接口继承实质上是规定一个兼容接口,外部调用者无需关系具体细节,只处理实现特定接口的对象,这在程序设计上叫做归一化。

  归一化使得高层的外部使用者可以不加区分处理所有接口兼容的对象集合

import abc
class All_file(metaclass=abc.ABCMeta):
    @abc.abstractmethod#定义接口,子类必须实现该功能,否则报错
    def read(self):
        pass
    @abc.abstractmethod
    def write(self):
        pass
class CDrom(All_file):
    def read(self):
        print("CDrom read")
    def write(self):
        print("CDrom write")
class Mem(All_file):#由于没有实现write功能,会报错
    def read(self):
        print("mem read")

5.继承顺序

  Python的类如果继承了多个类,那么继承顺序的寻找方式有两种:深度优先和广度优先。经典类会按深度优先方式查找,新式类会按照广度优先的方式查找。

class A:
    # def test(self):
    #     print("A")
    pass
class B(A):
    # def test(self):
    #     print("B")
    pass
class C(A):
    # def test(self):
    #     print("C")
    pass
class D(B):
    # def test(self):
    #     print("D")
    pass
class E(C):
    # def test(self):
    #     print("E")
    pass
class F(D,E):
    def test(self):
        print("F")
    # pass
f1 = F()
f1.test()#2.7 深度优先  F-->D-->B-->A-->E-->C-->报错;3.5 广度优先  F-->D-->B-->E-->C--A-->报错
print(F.__mro__)
print(F.mro() )

  对于定义的每一个类,Python会计算出一个方法解析顺序(mro)列表,这个mro列表就是一个简单的所有基类的线性顺序列表

  为了实现继承,Python会在mro列表中从左到右查找基类,mro列表的构造是通过一个C3线性化算法来实现的。

  所有的mro列表遵循三条准则:(1)子类会先于父类被检查;(2)多个父类会根据他们在列表中的顺序被检查;(3)如果下一个类存在两个选择,优先选择第一个。

6.子类中调用父类的方法

  子类继承了父类的方法并想基于原有的基础修改,就要在子类中调用父类的方法

class Vehical:
    def __init__(self,name,speed,load,power):
        self.name = name
        self.speed = speed
        self.load = load
        self.power = power
    def run(self):
        print("开动啦")
class Subway(Vehical):
    def __init__(self,name,speed,load,power,line):
        # Vehical.__init__(self,name,speed,load,power)
        # super().__init__(name,speed,load,power)
        super(Subway,self).__init__(name,speed,load,power)
        self.line = line
    def show_info(self):
        print(self.name,self.speed,self.load,self.power,self.line)
    def run(self):
        # Vehical.run(self)
        super().run()
        print("%s %s 开动啦" %(self.name,self.line))
line3=Subway("天津地铁","80km/h","500","electricity",3)
line3.show_info()
line3.run()

八、面向对象编程三大特征之多态

1.多态

  由不同的类实例化得到的对象,调用同一个方法,执行逻辑不同,多态是调用方法的技巧,不会影响到类的内部设计,可以增加代码的外部调用灵活度

class H2O:
    def __init__(self,name,temperature):
        self.name = name
        self.temperature = temperature
    def turn_shape(self):
        if self.temperature<=0:
            print("当前温度是%s℃,变成%s" %(self.temperature,self.name))
        if self.temperature >0 and self.temperature<100:
            print("当前温度是%s℃,变成%s" % (self.temperature, self.name))
        if self.temperature >= 100:
            print("当前温度是%s℃,变成%s" % (self.temperature, self.name))
class Water(H2O):
    pass
class Ice(H2O):
    pass
class Steam(H2O):
    pass

w1 = Water("",50)
i1 = Ice("",-30)
s1 = Steam("蒸汽",5000)
def func(obj):
    obj.turn_shape()
func(w1)

九、面向对象编程三大特性之封装

1.什么是封装

  封装(Encapsulation)是为了保护隐私,对具体对象的一种抽象,即将某些部分隐藏起来,在程序外部看不到,其含义是其他程序无法调用。

2.两个约定

  (1)单下划线开头的名字都是内部私有的

  (2)双下划线开头的属性在继承给子类时,子类无法覆盖

3.用法

  大多数情况下,用单下划线开头,当你清楚代码会设计到子类,并且有些内部属性应该在子类中隐藏,使用双下划线

4.代码

#单线划线
class People:
    _star = "earth"
    def __init__(self,id,name,age,salary):
        self.id = id
        self.name = name
        self.age = age
        self.salary = salary
    def get_id(self):
        pass
print(People._star)
p1 = People(112233,"大嘴",24,0)
print(p1._star)

#双下划线
class People:
    __star="earth"
    def __init__(self,id,name,age,salary):
        self.id = id
        self.name = name
        self.age = age
        self.salary = salary
    def get_id(self):
        pass
p1 = People(112233,"大嘴",24,0)
print(People.__dict__)
print(p1._People__star)

猜你喜欢

转载自www.cnblogs.com/hechengwei/p/9061453.html