十二、类与面向对象

一、面向对象

Python也是面向对象的语言,与java一样,面向对象具备三大特点,封装、继承、多态

1.类与对象

  • 类是对象的模具,确定对象将会拥有的属性和行为
  • 对象是类的具体实例表现

2.封装

封装就是隐藏具体信息及实现细节,提供访问接口

  • 特点:
    • 只能通过规定的方法访问数据
    • 隐藏类的实例细节,方便修改和实现

3.继承

继承是一种类与类之间的关系,使用已经存在的类的定义作为基础来创建新的类(父类与子类),子类的定义可以增加新的数据或新的功能,也可以用父类的功能,但不能选择性的继承父类

  • 满足A is a B的关系就可以形成继承关系
  • 特点:
    • 利于代码服用
    • 便于开发、维护

4.多态

多态意味着允许不同类的对象对同一消息作出不同的相应

  • 实现的必要条件:
    • 满足继承关系
    • 父类引用指向子类对象

二、类的定义及实现

类是面向对象的基础,是数据的一种类型,类的实例称之为对象,类中有很多属性和方法,开发者可以自定义类

1.类的定义(简陋)

class Animal(object):
    """动物类"""
    pass
  • 每个类都默认继承object类,可以不写明,但是规范要求写出来

2.类的特殊方法与属性

在Python中,每个类都有默认的几个特殊方法和属性,可以通过dir(类名)或者help(类名)来查看,主要有以下几个

  • 构造函数:

    def __init__(self, name, *args, **kwargs):
        self.name = name
        pass
    
    • 定义类时,如果没有去定义构造函数,Python默认定义一个构造函数__init__(self)
    • 如果定义了构造函数,Python则不会再创建__init__(self)
    • 在定义构造函数时,没有设置参数的默认值在创建实例时必须传入对应的参数,设置了默认值的参数可以不传…另外,设置默认值的参数必须放在必传参数的后面
    class Animal(object):
    
    def __init__(self, name, age=None):
        self.name = name
        self.age = age
    
    pass
    
  • 析构函数:用于删除类的对象,一般不用去定义,也不会去调用,Python有自己的垃圾处理机制,会自动去管理内存,自动去调用析构函数,这里只稍作了解

def __del__(self):
    pass
  • 对象描述函数__str__(self):当通过print(obj)直接打印对象时,会打印出一行对象内存地址相关的字符串,如果想要打出指定的内容,可以定义__str__(self)函数并返回需要显示的字符串对象,这样直接打印对象时,就会打印出该函数返回的结果
  • 常用的特殊属性:
    • __doc__:文档信息,注释等内容
    • __module__:模块信息,注意,如果是调用的是当前模块的类,则会显示__mian__
    • __bases__:继承于哪个类
    • __file__: 文件全路径,经常使用os.path.abspath(__file__)来获取当前文件全路径
    • …可以通过dir(类名)自行了解
    from test_class import Animal
    
    
    print(Animal.__doc__)
    print(Animal.__module__)
    print(Animal.__bases__)
    
    在这里插入图片描述

3.自定义类的实现

class Cat(object):
    """
    自定义动物类
    """

    flag = "我是共有属性"
    __flag2 = "我是私有的"
    _flag3 = "我不能导入"

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

    def get_flag2(self):
        return self.__flag2

    def get_age(self):
        return self.__age

    def set_age(self, age):
        self.__age = age

    def show_info(self):
        info = '我是一只喵,我叫{},{}岁'.format(self.name, self.__age)
        print(info)
        
    def __private_func(self):
        print("我是私有方法")
        
    @property
    def flag3(self):
        return self._flag3
  • 类变量:Python中,定义在类里面且在该类的方法体外的属性为类属性,可以直接通过该类调用,如Cat.flag,也可以通过实例去调用cat.flag
    • 通过类对类变量进行赋值时,程序中的所有类变量都会跟着改变,但是如果实例对象已经对该类变量进行过赋值操作,那么会在该实例中生成一个与该类变量同名的实例变量,此时通过类对类变量进行赋值时不会对该实例的此变量有影响
    • 通过实例对象对类变量进行赋值,只会对当前实例对象有影响
  • 实例变量:定义在构造函数中的属性则为实例属性,可以被继承,外界能通过实例对象直接调用
  • 私有变量和方法:Python规定,在类中定义变量或方法时,如果以双下划线开头如__name,则该变量或方法为私有变量/方法,外界无法直接访问,子类无法继承,只能通过该类提供的接口来访问该变量
  • 禁止导入变量和方法:Python规定,在类中定义变量或方法时,如果以单下划线开头如_name,则该变量或方法无法通过from somemodule import *导入,子类可以继承,外界可以通过实例对象访问

4.类的实例化及判定

  • 实例化
cat = Cat('我是小白白', 23)
  • 类的实例判断
    • 语法:isinstance(实例名, 类名),结果返回布尔值
    cat = Cat('我是小白白', 23)
    print(isinstance(cat, Cat))
    

5.类实例的扩展

与Java等强类型语言不同,Python可以在运行时对类的实例的属性和函数进行修改、扩展

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

    def show_info(self):
        print("我叫{0}, 今年{1}岁".format(self.name, self.age))


def eat():
    print("我吃东西")


if __name__ == "__main__":
    p = Person('Schuyler', 22)
    p.show_info()
    p.code = 2222
    p.eat = eat
    p.eat()
    print(p.code)

在这里插入图片描述


三、类的继承和多态

class Person(object):
    """人类"""
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def job(self):
        print("人类都有自己的工作")


class Student(Person):
    """学生类"""
    def job(self):
        super(Student, self).job()
        print("学生的工作就是读书")


if __name__ == "__main__":
    stu = Student("Schuyler", "26")
    stu.job()
    print(issubclass(Student, Person))

在这里插入图片描述

1.继承

  • 在Python中,所有的类都默认继承了object类,当需要指定继承某个类的时候,在定义时直接将父类名称作为该类的参数即可,如class Student(Person)
  • 当子类继承父类后将会继承父类的所有方法,和属性,包括构造函数。当然子类可以对方法进行重写

2.调用父类方法

  • 语法:super().methodName()
    • 此处不需要传入参数self
  • 语法:super(当前类名, self).methodName(),与这种方式相比,推荐用第一种,更好维护,比如当前类名改动时,不需要去做过多的修改
  • 如果有多个父类,也可以通过父类名.methodName(self)来调用,如Person.__init__(self, name)

3.子父类判定-issubclass

  • 语法:issubclass(子类名, 父类名),返回布尔值

4.多重继承

  • Python中多重继承只需要在定义子类时将多个需要继承的父类名作为子类的形参即可
  • 当多重继承时,父类中有存在同名函数时,按形参的排列顺序进行优先调用
    class Person(object):
    def eat(self):
        print("人需要吃饭")
    
    def talk(self):
        print("人类需要沟通")
    
    
    class Boy(object):
        def sex(self):
            print("我是个男的")
    
        def talk(self):
            print("男人之间用拳头交流")
    
    
    class Man(Boy, Person):
        pass
    
    
    if __name__ == "__main__":
        man = Man()
        man.sex()
        man.eat()
        man.talk()
    
    在这里插入图片描述

5.多态

  • 每当无需知道对象是什么样的就能对其执行操作时,都是多态在起作用
  • 即同一个方法在不同类中实现不同的功能(函数的重写)

四、类的高级特性

1.@property

  • 1.@property可以将类的方法当作属性调用即obj.函数名,这样可以配合私有变量的使用,让私有变量的调用更为便利,灵活
  • 2.@property可以配合@函数名.setter注解来对变量的设置进行校验操作,其中函数名指的是@property注解的函数名
  • 3.示例
    class Person(object):
    def __init__(self, name, age):
        self.name = name
        self.__age = age
    
    @property
    def age(self):
        """ 私有变量的访问 """
        return self.__age
    
    @age.setter
    def age(self, value):
        """ 私有变量的设置校验 """
        if not isinstance(value, int):
            print("只能输入数字哦")
        elif value < 0:
            print("岁数不能是负数哦")
        else:
            self.__age = value
    
    
    if __name__ == "__main__":
        p = Person("小明", 25)
        p.age = -1
        p.age = "o"
        p.age = 2
        print(p.age)
    
    在这里插入图片描述

2.__slots__

  • 1.__slots__为指定的类设置一个静态属性列表,此时在定义类时,属性的名称需要根据该列表来定义
  • 2.__slots__为属性很少的类节约内存空间
  • 3.__slots__的赋值内容是一个元祖数据
  • 4.使用__slots__后除了元祖数据里指定内容外,不能给实例添加新的属性或者方法(这里解释一下,与Java不同,Python可以在运行过程中对类的实例随意的添加函数和属性,为了防止这一点,可以通过__slots__来控制)
  • 5.使用__slots__的类的子类也要遵循父类的规则,如果子类需要扩展,可以在子类中也定义__slots__,这样会在父类的基础上扩充
  • 示例
from time import sleep

class Person(object):
    __slots__ = ('age', 'name')

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

    def show_info(self):
        print("我叫{0}, 今年{1}岁".format(self.name, self.age))


def eat():
    print("我吃东西")


if __name__ == "__main__":
    p = Person('Schuyler', 22)
    p.show_info()
    p.name = "小明"
    p.age = 11
    p.show_info()
    #为了方便展示错误信息,这里停顿一秒
    sleep(1)
    p.code = 2222
    p.eat = eat

在这里插入图片描述

3.类的静态方法、类方法、实例方法

  • 静态方法@staticmethod
    • @staticmethod该注解使得类的方法可以直接通过类来调用,在定义该函数时,无法传入self
    class Person(object):
    def __init__(self, name, age):
        self.name = name
        self.age = age
    
    def show_info(self):
        print("我叫{0}, 今年{1}岁".format(self.name, self.age))
    
    @staticmethod
    def speak():
        print("我是个静态方法")
    
    
    if __name__ == "__main__":
        Person.speak()
    
    在这里插入图片描述

2.类方法@classmethod

  • 1.使用@classmethod注解的方法为类方法,与实例函数区别于传入的不再是类实例(self)而是类本身(cls)
  • 2.类方法可以直接调用类属性,但是无法直接调用实例属性
  • 3.类方法虽然无法直接调用实例属性,但是可以通过cls(...)来构造该类的实例从而去调用实例属性
  • 4.与静态方法类似,类方法也可以直接通过类来调用,但是静态方法无法直接调用类属性
class Person(object):

    value = "我是类属性"

    def __init__(self, name, age):
        self.name = name
        self.age = age
        
    @classmethod
    def class_method(cls):
        print(cls.value, "名字:{}".format(cls("Schuyler", 11).name))


if __name__ == "__main__":
    Person.class_method()

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值