wuxiaobo

Python之路(七)——Python OOP(基础篇)

本节内容:

  1. 面向对象(OO)是思想
  2. 类和对象
  3. 属性和方法
  4. 静态属性与@Property
  5. 类方法
  6. 静态方法
  7. 抽象与抽象类
  8. 接口
  9. 继承
  10. 组合
  11. 多态
  12. 封装

一、面向对象(OO)是思想

def person(name, age, sex):
    #初始化
    def init(name, age, sex):
        person ={}
        person["name"] = name
        person["age"] = age
        person["sex"] = sex
        person["say"] = say
        person["eat"] = eat
        return person

    def say(person):
        print("My name is :%s,i\'am %s old." % (person["name"], person["age"]))

    def eat(person):
        print("%s is eating" % person["name"])

    return init(name, age, sex)


p_lw = person("老王", 42, \'男\')
print(p_lw["name"])
p_lw["eat"](p_lw)
#结果输出:
# 老王
# 老王 is eating

结论:OO是一种思想,class 定义类只是更加方便地实现OO思想而不是只有定义了class才是面向对象编程,同样即使定义了class也有可能不是面向对象编程  

二、类和对象

前言

  • 面向对象是一种思想、方法论,与面向过程、函数式编程一同组成了主流的三种编程思想
  • 面向对象涵盖:面向对象分析——OOA 、面向对象设计——OOD、面向对象编程——OOP,本节重点记录Python中的OOP方法而非OO思想
  • 类、对象的属于都是发生在特定的场景下,即不是绝对的

类与对象

类:一组事物共有的属性和方法的集合(有时也叫属性集合:数据属性,方法属性),可理解为蓝图、模板。

对象:一个个单独的事物,也叫实例

class Person:
    def __init__(self,name,age,sex):
        self.name = name
        self.age = age
        self.sex = sex

    def say(self):
        print("My name is :%s,i\'am %s old." %(self.name,self.age))

    def eat(self):
        print("%s is eating" %self.name)

#实例化一个Person 对象——老王
p_lw = Person("老王", 42, \'男\')
#调用
p_lw.eat() # 老王 is eating

初始化构造函数__init__

用于对象实例化且被隐式调用,有如下特性:

  • 名称唯一:__init__
  • 至少有一个参数self,表示实例本身且在对象实例化时自动传入对象名(地址)
  • 没有返回值

self关键字用于指代当前处理的对象

class Student():
    def __init__(self):
        print("当前对象的地址是:%s"%self)

if __name__ == \'__main__\':
    student1 = Student()
    print(student1)
    student2 = Student()
    print(student2)

#结果为:
# 当前对象的地址是:<__main__.Student object at 0x000002A415378AC8>
# <__main__.Student object at 0x000002A415378AC8>
# 当前对象的地址是:<__main__.Student object at 0x000002A415378E10>
# <__main__.Student object at 0x000002A415378E10>

三、属性和方法

类的属性操作

#类属性和方法
class Chinese:
    country = "中国"

    def __init__(self,name,age):
        self.name = name
        self.age = age

    def chadui(self):
        print("%s 又他妈插队了!" %self.name)

#属性增删改查

print(Chinese.country) #中国
p1 = Chinese("王八蛋", 22)
print(p1.country) # 中国
print(p1.__dict__) # {\'name\': \'王八蛋\', \'age\': 22}

#结论: .操作符 在本对象中__dict__查找,没有 找类

Chinese.fs = "黄种人"
print(Chinese.__dict__)# 新加  \'fs\': \'黄种人\'
print(p1.fs) #同样可以找到 黄种人

#修改
Chinese.country = "*"
print(Chinese.country, p1.country) # * *


def sleep(self):
    print("%s 正在睡觉." %self.name) # 王八蛋 正在睡觉.

Chinese.sleep = sleep
p1.sleep()

#结论: Python对OOP 方面的语法不是严格控制,需要程序员自行控制 

对象的属性操作

class Chinese:
    country = "中国"

    def __init__(self,name,age):
        self.name = name
        self.age = age

    def chadui(self):
        print("%s 有他妈插队了!" %self.name)


#实例化一个对象
lw = Chinese("老王", 42)
print(lw.__dict__) # {\'name\': \'老王\', \'age\': 42}

lw.aihao = "抽烟"
print(lw.__dict__) # {\'name\': \'老王\', \'age\': 42, \'aihao\': \'抽烟\'}
print(Chinese.__dict__) #没有aihaoshuxing

def sahuang(self):
    print("%s 就喜欢撒谎" %self.name)

lw.sahuang = sahuang
lw.sahuang(lw) # 老王 就喜欢撒谎

#上述对象函数与类无关业余其他对象无关,仅语法可以,没有实际意义

print(lw.__dict__)
print(Chinese.__dict__) # 没有撒谎函数

#拿了绿卡
lw.country = "美国" #赋值语句都是新建,对象不能修改类属性
print(lw.__dict__) 
# {\'name\': \'老王\', \'age\': 42, \'aihao\': \'抽烟\', \'sahuang\': <function sahuang at 0x000001D6B602B6A8>, \'country\': \'美国\'}

# 删除——遣回中国
del lw.country
print(lw.country) #中国

结论:对象的属性单独维护在对象中,独立于类的属性。对象查找属性时先从本对象__dict__中查找,没有在到类__dict__中查找

四、静态属性

这个专有名词歧义很大,首先类有自己的属性,这些属性相对于对象来说就是静态的属性。而这里的静态属性是指:在类的方法(仅有self参数)前加入装饰器@property

class Room:

    style = "别墅" #类属性

    def __init__(self, name, addr, length, width, height):
        self.name = name
        self.addr =addr
        self.length = length
        self.width = width
        self.height = height

    def get_height(self):
        print("%s 房间的高度为: %s" % (self.name, self.height))

    def ruzhu(self, owner):
        print("%s 入住了 %s" % (owner, self.name))

    @property
    def area(self):
        return self.length * self.width
    # def cal_area(self):
    #     return self.length * self.width



room1 = Room("山村人家" ,"峦峰岗",100,50,20)

room1.ruzhu("张三") # 张三 入住了 山村人家

#求room1 的面积
# print(room1.cal_area()) # 5000

#方法二、使用@property 装饰器
print(room1.area) # 5000
print(room1.__dict__) #没有area 属性的
#从外部看来就是一个对象属性

结论(个人理解):Python 中 类其实可以没有属性,即:属性在有值的情况下才有意义。所以类中仅保存这类事物的共有方法即可。例如:一个人他虽然有国籍,名字,年龄,但是人这个类其实是没有这些的,Python 中把这类属性直接保存在每个对象属性中,那有时候需要对象的间接属性,如:取Room 占多少平米,通过@property 修饰符把一个方法转换成一个属性(至少表面上看起来是这样的)

五、类方法

类中的方法不与对象绑定(默认绑定即第一个参数为self),而与类自身进行绑定(self ->cls)。可以定制化调用__init__方法创建对象

class Dog:
    def __int__(self,name,color):
        self.name = name
        self.color = color

    def jiao(self):
        print("%:汪汪汪!" %self.name)
    @classmethod
    def about_me(cls):
        print("这个是一个Dog 类")

Dog.about_me()

六、静态方法

类中不与对象(self)也不与类(cls) 绑定的方法。一般是作为工具方法来使用。

import time
class  TimeTest:
    def __init__(self,hour,min,sec):
        self.hour = hour
        self.min = min
        self.sec = sec

    #静态方法,仅作用域限定在类中,其实就是一个独立的函数,可以有参数
    @staticmethod
    def show_currtime(var1,var2):
        print(time.asctime())

TimeTest.show_currtime(1,2)

七、抽象与抽象类

抽象:现实生活中首先识别到一个个具体的对象,在对象的基础上提取共有属性。这个动作也叫泛化

OOA(面向对象分析)步骤<简述>:

  1. 识别一个个对象
  2. 提取对象特征,对象间的关系
  3. 抽象特征,组成类
  4. 循环第三步骤,形成类的层级关系图

抽象类

类的抽象,更抽象地描述事物

import abc
#抽象类,不能被实例化,只能被继承使用
class Shape_2D(metaclass=abc.ABCMeta):

    type = "二维图形"
    #初始化方法被抽象定义,so 不能实例化一个Shape_2D 对象
    @abc.abstractmethod
    def __init__(self):
        \'\'\'
        抽象方法
        \'\'\'
        pass

class Circle(Shape_2D):
    type="圆"
    def __init__(self,x,y,radius):
        pass

class Line(Shape_2D):
    type = "线"
    def __init__(self,x1,y1,x2,y2):
        pass

c1 = Circle(1,2,3)
print(c1.type) # 圆

八、接口

语法与抽象类相同,表示一组特定功能提供统一的对外功能

import  abc
class I_Pay(metaclass=abc.ABCMeta):
    #支付
    @abc.abstractmethod
    def pay(self):
        pass
    
    #退款
    @abc.abstractmethod
    def tuikuan(self):
        pass
    
class AppleMobile(I_Pay):
    def pay(self):
        print("苹果手机支付")
    def tuikuan(self):
        print("苹果手机退款")

class XMMobile(I_Pay):
    def pay(self):
        print("小米手机支付")
    def tuikuan(self):
        print("小米手机退款")

抽象类 vs 接口

  • 抽象类:还是一个类,仅对类进一步抽象;接口:特定功能的规范性描述
  • 抽象类:描述什么是什么关系,例:战斗机、民用机是飞机,麻雀是鸟;接口:描述什么有什么功能的关系,例:飞机,鸟都有飞翔的功能

注意:在OO中所有概念(类,抽象类,接口等)都是在特定场景下,而非绝对

九、继承

即抽象的逆过程。 对象与类 \'延续共有属性\' 叫 : 实例化; 类与类 \'延续共有属性 \'叫继承,存在父-子关系。

MRO

一个元祖,记录继承顺序。

#继承树
    #   A
    # B   C
    # D   E
    #   F

class A:
    def test(self):
        print("A")

class B(A):
    def test(self):
        print("B")
class C(A):
    def test(self):
        print("C")

class D(B):
    def test(self):
        print("D")
    pass
class E(C):
    def test(self):
        print("E")

class F(D,E):
    def test(self):
        print("F")
f = F()
f.test()

#新式类广度优先:左边 F->D->B->E->C->A
#老式深度优先: F->D->B->A->E->C

print(F.__mro__)
# (<class \'__main__.F\'>, <class \'__main__.D\'>, <class \'__main__.B\'>, <class \'__main__.E\'>,
# <class \'__main__.C\'>, <class \'__main__.A\'>, <class \'object\'>)

super关键字

子类引用父类内容,使用super关键字

class Stuff:
    def __init__(self,name,age,sex):
        self.name = name
        self.age = age
        self.sex = sex

    def say(self):
        print("我的名字 %s" %self.name)

class Teacher(Stuff):
    def __init__(self,name,age,sex,zc):
        super().__init__(name,age,sex)
        # super(Teacher, self).__init__(name,age,sex)
        self.zc = zc



t1 = Teacher("老张",12,\'男\',\'副高\')
t1.say() # 我的名字 老张

十、组合

类与类的关系除了继承,还有组合、聚合、关联

  • 组合:一个类是其中一个类的组成部分,且紧密相关。例:Linux硬盘操作模块与LinuxOS内核,独立于Linux内核硬盘操作这个模块无法使用
  • 聚合:一个类是一个类的一部分,但是可独立使用。例:发动机与汽车,发动机可以独立出来使用
  • 关联:最常用,两个类都是独立的,但存在某种关系。例如:学校老师绩效考核系统中,学校是一个类,老师是一个类,但这里的老师与学校存在关联

十一、多态

功能函数参数采用父类类型定义,调用时根据传入不同的子类对象实际调用其子类自身的方法,从而同一函数看起来呈现不同的形态。没有继承就没有多态

#多态
class Animal:
    def __init__(self):
        pass

    def move(self):
        pass

class Dog(Animal):
    def __init__(self):
        pass
    def move(self):
        print("Dog  is running")

class Bird(Animal):
    def __init__(self):
        pass
    def move(self):
        print("Bird  is flying")

#函数让传输的动物移动两次
def run(animal):
    animal.move()
    animal.move()

if __name__ == "__main__":
    dog1 = Dog()
    bird1 = Bird()
    run(dog1)
    run(bird1)

#结果:
# Dog  is running
# Dog  is running
# Bird  is flying
# Bird  is flying
#好处: 调用程序仅判断一类事物,仅针对同一类型进行处理。例如:新增一个动物,本程序亦然运行无恙  

十二、封装

目的

封装的目的就是隐藏复杂性。例如: 撒尿是经过一系列的动作,但是你本身是不清楚的(医生除外),掏出你的枪开干就完事了。

封装的最终结果做到内外有别,仅给出对方需要的数据,不少也不多。

方法

Python 对封装没有提供特有的关键字,仅通过变量名和约定来完成

  • _foo:保护的方法/属性 protect
  • __foo:私有方法/属性,只允许类的内部访问 private
  • foo:普通变量名,可以被外部访问  public
class Employee:
    def __init__(self,name,age,race,salary):
        self.name = name
        self.age = age
        self._race = race
        self.__salary = salary



e = Employee(\'老王\',55,\'黑人\',50000)
print(e._race) # 可以访问但不建议使用,提供给子类访问的
# print(e.__salary) # AttributeError: \'Employee\' object has no attribute \'_salary\'
print(e._Employee__salary) # 一定要看也可以

#结论: Python 中很多功能都是不强制,讲究约定俗称  

 

分类:

技术点:

相关文章: