九.深入面向对象

隐藏数据

引入
class Cat(object):
    def __init__(self, new_name, new_age):
        self.name = new_name
        self.age = new_age

    def print_info(self):
        print("我叫%s,今年%s了" % (self.name, self.age))


# 创建猫对象
cat = Cat("波斯猫", 4)
# 调用方法
cat.print_info()
# 尝试修改属性
cat.age = -10
# 调用方法
cat.print_info()

如果运行上述代码,会发现,第二次输出的信息有误,其为"我交波斯猫,今年-10岁了"

之所以出现这样的错误,究其原因是因为:我们通过对象直接给实例属性赋值的这种方式容易出错

如果在赋值的时候,是通过一个实例方法的调用,在方法中对数据进行严格的检查,合格的数据可以给属性设置,不合格的数据就提醒开发者,这样一来就能够保证数据的准确性了

那该怎样实现呢

答:

  1. 设置属性为私有属性
  2. 添加额外对属性操作的方法
实现对数据的隐藏

想要实现上述阐述的那个功能,我们需要做2件事情

  1. 设置属性为私有属性
  2. 添加额外对属性操作的方法

设置私有属性

在Python中,如果想要设置为私有的属性,那么仅仅需要在定义属性时在前面加两个下划线__即可

示例如下:

class 类名(object):
    def __init__(self):
        self.name = "张三"
        self.__age = 18  # 这个属性就是私有属性

既然有了私有属性,那对象能够直接操作它呢?

答:不能,否则就没有私有的作用了

示例如下:

class Teacher(object):
    def __init__(self):
        self.name = "张三"
        self.__age = 18  # 这个属性就是私有属性


t = Teacher()
print(t.name)  # 能够获取
print(t.__age)  # 此时会程序报错,因为__age是私有属性,不能通过对象直接操作

添加额外对属性操作的方法

想要实现对私有属性的操作,我们需要定义方法,在方法中操作

示例:

class Teacher(object):
    def __init__(self):
        self.name = "张三"
        self.__age = 18  # 这个属性就是私有属性

    def set_age(self, new_age):
        if 1 <= new_age <= 120:
            self.__age = new_age
            print("设置年龄成功")
        else:
            print("年龄数据有误...")
    
    def get_age(self):
        return self.__age


t = Teacher()
t.set_age(20)  # 设置年龄
print(t.get_age())  # 获取年龄
简单总结

1.操作属性有2种方法

  • 直接通过对象名修改
对象名.属性名 = 数据
  • 通过方法间接修改
对象名.方法名(数据)

2.通过使用方法来进行修改,就可以在方法中进行数据合法性的检查

3.通过__可以将属性变为私有属性,这样就防止了通过对象直接操作属性时可能带来的隐患

隐藏功能

引入

生活中我们肯定去过银行办理过业务,我们可以从银行的大门进入大厅,取号等待办理业务,可以在大厅里来回走动,这个区域是所有人都可以随意进出的;而银行办公人员工作的地方,只能有相应的权限的办公人员才能进出,这个区域对于外来办理业务的人员来说是禁止的。

通过上述的描述,大家能够理解了一件事情,即访问的地方不同需要的权限不同

那么试想,一个较大软件系统肯定有很多个可以让用户直接调用的接口(API可以简单理解为方法)这些接口可以任意调用,而有些接口就不能使用

在Python中,我们把可以通过对象直接调用的方法叫做公有方法,不能通过对象直接调用的方法叫做私有方法

实现对方法的隐藏

对于定义私有方法的方式与定义私有属性基本相同,就是在方法的前面添加__(即2个下划线_

示例如下:

class BankService(object):
    def __bank_2_bank(self, money):
        print("这里是银行之间的转账代码....")
        return True

    def transfer(self):
        money = int(input("请输入转账金额:"))
        if money > 100000:
            if self.__bank_2_bank(money):
                print("转账成功")
            else:
                print("转账失败...")
        else:
            print("都没钱,还转什么啊!自己留着花吧!")


bank_service = BankService()
bank_service.transfer()  # 可以调用,是公有方法

运行测试(转账成功)

请输入转账金额:20000000
这里是银行之间的转账代码....
转账成功

运行测试(转账失败)

请输入转账金额:100
都没钱,还转什么啊!自己留着花吧!
注意点
  • Python中没有像C++publicprivate这些关键字来区别公有私有,它是以命名方式来区分,如果在名字前面加了2个下划线__,则表明该属性是私有,否则为公有
直接调用私有方法所出现的问题

代码示例:

class BankService(object):
    def __bank_2_bank(self, money):
        print("这里是银行之间的转账代码....")
        return True

    def transfer(self):
        money = int(input("请输入转账金额:"))
        if money > 100000:
            if self.__bank_2_bank(money):
                print("转账成功")
            else:
                print("转账失败...")
        else:
            print("都没钱,还转什么啊!自己留着花吧!")


bank_service = BankService()
# bank_service.transfer()  # 可以调用,是公有方法
bank_service.__bank_2_bank()  # 不可以调用,是私有方法

运行结果:

AttributeError                            Traceback (most recent call last)
Cell In [1], line 19
     17 bank_service = BankService()
     18 # bank_service.transfer()  # 可以调用,是公有方法
---> 19 bank_service.__bank_2_bank()

AttributeError: 'BankService' object has no attribute '__bank_2_bank'

对象关联

引入

大家知道,我们在上学的时候,每个同学是一个对象,那么教室也是一个对象对吗?每个同学肯定是属于某一个教室的对吧,例如张三是205班的;那么怎样才能用代码来实现他们之间的关系呢

如下代码,创建了1个教室对象,1个学生对象,该怎样将它们2个联系起来呢?

class Classroom(object):
    def __init__(self, name):
        self.classroom_name = name


class Student(object):
    def __init__(self, name):
        self.student_name = name


# 创建一个教室对象
class205 = Classroom("205班")

# 创建一个学生对象
stu01 = Student("学生1")
将两个对象进行关联

在上述的代码中,我们发现如果当前的教室对象与学生对象是没有任何关系关联的,如果想要实现学生属于教室,那么只需要2步就能实现

  1. 搞清楚谁属于谁,例如上述示例中,学生属于教室
  2. 在范围大的那个对象中,定义一个属性存储范围小的对象引用即可

示例一:

class Classroom(object):
    def __init__(self, name):
        self.classroom_name = name


class Student(object):
    def __init__(self, name):
        self.student_name = name


# 创建一个教室对象
class205 = Classroom("205班")

# 创建一个学生对象
stu01 = Student("学生1")

# 直接给教室对象添加属性
class205.stu = stu01
调用关联的对象

上述代码已经完成了对象学生与教室的关联,那么怎样调用呢?格式如下:

# 如果A对象中的某个属性指向了B对象,那么调用方式
A.xxx  # 此时就是指的B对象

# 如果想要调用B对象中的某方法,那么就再接着.yyy方法即可
A.xxx.yyy()

代码示例:

class Classroom(object):
    def __init__(self, name):
        self.classroom_name = name
        self.stu = None  # 一般情况下在本类的其它方法中用到的实例属性,都要在__init__方法中定义

    def add_new_stu(self, stu):
        """定义新方法用来完成关联"""
        self.stu = stu


class Student(object):
    def __init__(self, name):
        self.student_name = name


# 创建一个教室对象
class205 = Classroom("205班")

# 创建一个学生对象
stu01 = Student("学生1")

# 调用方法将学生添加到对象中
class205.add_new_stu(stu01)

# 调用学生的姓名
# 205教室.学生.姓名
print(class205.stu.student_name)

运行结果:

学生1
关联多个对象

既然关联1个对象搞懂了,那么关联多个也就手到擒来,方式如下:

  1. 在范围大的那个对象中再定义一个新的属性,通过设置属性指向新的对象
  2. 如果关联的新的对象与之前关联的对象类型相同,可以考虑用列表、字典、集合等方式将它们关联

实现将多个学生关联到一个教室:

class Classroom(object):
    def __init__(self, name):
        self.classroom_name = name
        self.stus = []  # 一般情况下在本类的其它方法中用到的实例属性,都要在__init__方法中定义

    def add_new_stu(self, stu):
        """定义新方法用来完成关联"""
        # self.stu = stu
        self.stus.append(stu)


class Student(object):
    def __init__(self, name):
        self.student_name = name


# 创建一个教室对象
class205 = Classroom("205班")

# 创建多个学生对象
stu01 = Student("学生1")
stu02 = Student("学生2")
stu03 = Student("学生3")

# 调用方法将学生添加到对象中
class205.add_new_stu(stu01)
class205.add_new_stu(stu02)
class205.add_new_stu(stu03)

# 调用学生的姓名
# 205教室.列表[下标].姓名
print(class205.stus[0].student_name)
print(class205.stus[1].student_name)
print(class205.stus[2].student_name)

面向对象中的继承

引入

继承,通俗的说就是不劳而获,不需要付出一下子什么都有了

Python中也有继承这个功能,它能够实现一个类中可以使用另外一个类中的代码

在开发较大的项目时,往往需要多个类实现,当我们定义一个新类时如果这个新的类的功能与之前某个类功能很类似,此时通过使用继承可以让新类不用写代码或者写很少的代码,就实现了想要的所有功能,这样一来编写的代码少了也就提高开发效率

格式定义

生活中,如果子女想要继承父辈的遗产,往往需要一定的证明素材,这样才有合法性;同理Python中想要表示出谁继承谁,也需要一定的格式来规定,这种方式就是在定义类的()中写上父类的名字。

如下示例:

class Animal:
    pass


class Dog(Animal):  # 继承Animal
    pass


class Cat(Animal):  # 继承Animal
    pass


class BoSiCat(Cat):  # 继承Cat
    pass
在继承中的专用术语

为了更加清楚的标记处,谁继承了谁,我们用父类(基类)子类(派生类)来称呼它们

  • 父类:被继承的类
  • 子类:继承的类

代码示例:

# 父类
class A(object):
    def __init__(self):
        self.num = 10

    def print_num(self):
        print(self.num + 10)


# 子类
class B(A):
    pass


b = B()
print(b.num)
b.print_num()

运行结果:

10
20

从上述的运行结果来看,虽然类B没有写代码,但是依然能够正确的执行,这足以说明类B继承了类A的功能

单继承

单继承的概念

单继承,就是一个子类只继承一个父类

下面定义了2个类,Animal类是父类它编写了3个功能,Dog类是子类它继承了Animal的功能

class Animal:
    def eat(self):
        print("吃饭...")

    def drink(self):
        print("喝水...")

    def sleep(self):
        print("睡觉...")


class Dog(Animal):
    pass


dog = Dog()
dog.eat()
dog.drink()
dog.sleep()

运行上述代码,会看到如下效果:

吃饭...
喝水...
睡觉...
为类添加新功能

假如现在需要在上述的代码中,添加一个新的方法info,那么该写到父类中还是子类中呢?

写到父类

class Animal:
    def eat(self):
        print("吃饭...")

    def drink(self):
        print("喝水...")

    def sleep(self):
        print("睡觉...")

    def info(self):
        print("名字是:" + self.name + ", 年龄是:" + str(self.age))


class Dog(Animal):
    def __init__(self, name, age):
        self.name = name
        self.age = age


dog = Dog("小黑", 2)
dog.eat()
dog.drink()
dog.sleep()
dog.info()

运行效果如下:

吃饭...
喝水...
睡觉...
名字是:小黑, 年龄是:2

从上述的运行来看,虽然Animal类中没有name属性、age属性,但是可以在子类中添加这2个属性,只要方法被子类继承后,通过子类的对象调用时可以直接使用子类的属性

但是,要注意如果在父类Animal中如果没有nameage那么也就意味着不能直接创建Animal类的实例对象,否则当通过Animal的实例对象调用info方法时就会出现找不到nameage属性的问题

写到子类

class Animal:
    def eat(self):
        print("吃饭...")

    def drink(self):
        print("喝水...")

    def sleep(self):
        print("睡觉...")


class Dog(Animal):
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def info(self):
        print("名字是:" + self.name + ", 年龄是:" + str(self.age))


dog = Dog("小黑", 2)
dog.eat()
dog.drink()
dog.sleep()
dog.info()

运行效果如下:

吃饭...
喝水...
睡觉...
名字是:小黑, 年龄是:2
两种定义方式的区别
  1. 当前将新方法定义到子类中不会对父类造成影响
  2. 如果将新方法定义到父类中则继承这个父类的所有子类都会获得这个新方法, 但是有些子类并不需要
  3. 如果将新方法定义到父类中可能导致父类无法实例化

总结:

如果一个方法在多个子类中用到,那么就定义在父类的方法中,否则就定义在子类中。

多继承

多继承的概念

如果一个类继承了多个父类,那么这就是多继承

定义格式

单继承时在类()中写一个父类的名字,那么多继承就需要在()中写多个父类的名字而且用英文逗号,进行分割

class A:
    pass


class B:
    pass


class C(A, B):  # 继承了A、B类
    pass
多继承的应用

我们都知道今天的手机功能已经非常全面了,而且还在突风猛进的进化中,不仅有电话功能还有照相功能(参照照相机)、播放音乐(参照MP3)等,下面我们通过一步步的展示方式来揭晓多继承的真实作用价值所在。

首先我们定义一个普通的手机Telephone(即很久之前的功能机)

class Telephone:
    def call(self):
        """打电话"""
        print("正在打电话...")

    def answer(self):
        """接电话"""
        print("正在接电话...")

如果我们想要让手机拥有照相机的功能,此时就需要定义一个照相机类Camera

class Camera:
    def take_photo(self):
        """拍照功能"""
        print("正在拍照...")

然后让Telephone继承Camera

class Camera:
    def take_photo(self):
        """拍照功能"""
        print("正在拍照...")


class Telephone(Camera):
    def call(self):
        """打电话"""
        print("正在打电话...")

    def answer(self):
        """接电话"""
        print("正在接电话...")

创建一个手机对象,此时就手机就继承了拍照功能

class Camera:
    def take_photo(self):
        """拍照功能"""
        print("正在拍照...")


class Telephone(Camera):
    def call(self):
        """打电话"""
        print("正在打电话...")

    def answer(self):
        """接电话"""
        print("正在接电话...")


phone = Telephone()
phone.call()
phone.answer()
phone.take_photo()

运行效果:

正在打电话...
正在接电话...
正在拍照...

如果想要让手机拥有听音乐的功能,我们可以定于一个MP3

class MP3:
    def play_music(self):
        """播放音乐功能"""
        print("正在播放音乐...")

最后让手机Telephone类继承MP3类

class Camera:
    def take_photo(self):
        """拍照功能"""
        print("正在拍照...")


class MP3:
    def play_music(self):
        """播放音乐功能"""
        print("正在播放音乐...")


class Telephone(Camera, MP3):
    def call(self):
        """打电话"""
        print("正在打电话...")

    def answer(self):
        """接电话"""
        print("正在接电话...")


phone = Telephone()
phone.call()
phone.answer()
phone.take_photo()
phone.play_music()

带有音乐功能的手机就做好了,试试看:

正在打电话...
正在接电话...
正在拍照...
正在播放音乐...

重写

引入

我们知道一个子类如果继承了父类,那么当通过子类对象去调用一个方法时,如果子类对象中没有此方法,那么就会到继承的父类中查询,如果查询到有则进行调用

但是有时,我们发现子类继承的父类的方法不能100%满足子类对象的需求,则此时就需要在子类中定义一个与父类相同的名字的方法,此时子类对象调用这个方法时即使父类中有,但依然不会调用,而是调用子类中的方法。

什么是重写

我们把子类中定义了与父类中相同名字的方法,叫做重写(大白话就是:子类的方法覆盖了父类中的同名方法)

代码示例
class Father(object):
    def play_game(self):
        print("父类中的play_game")

    def drink(self):
        print("父类中的drink方法")


class Son(Father):
    def play_game(self):
        print("子类中的play_game")


father = Father()
father.play_game()  # 调用父类中的方法,因为对象是父类创建的

son = Son()
son.play_game()  # 调用子类中的方法,因为在子类中重写了play_game方法
son.drink()  # 调用父类中的方法,因为子类中并没有重写此方法

运行结果:

父类中的play_game
子类中的play_game
父类中的drink方法
注意事项

如果父类中的方法在子类继承时发现并不符合子类的需求,此时我们在子类中重写这个方法即可

切记:不要直接在父类中修改此方法,如果将父类中的方法改成你子类的功能,虽然子类创建的对象可以100%满足要求,但你并不能保证其它继承这个父类的子类也需要同样的功能,所以在以后的开发工作中,一个父类定义好了之后,就不要轻易的修改,否则继承它的子类都要进行修改,这个工作量是非常大的,请不要这么做

super 方法

引入

上一节课程我们知道,如果父类中的方法在派生的子类中不能满足其需求的话,可以在子类中通过重写解决这个问题

但是很多情况下,父类中的方法并不是全部一点都不能用,即子类的需求往往是在父类方法实现的功能基础上提出了更多的需求而已,此时如果我们在子类中重写此方法时就会发现出现了很多冗余的代码,这个问题该怎么解决呢?

答:在子类重写的方法中通过调用父类中被重写的方法

代码示例

示例一:

class Father(object):
    def play_game(self):
        print("父类中的play_game")


class Son(Father):
    def play_game(self):
        super().play_game()  # 先调用父类被重写的方法
        print("子类中的play_game")  # 然后再添加子类需要的新功能


son = Son()
son.play_game()  # 调用子类中的方法,因为在子类中重写了play_game方法

运行效果:

父类中的play_game
子类中的play_game

示例二:

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

    def __str__(self):
        return "%s的年龄是: %d" % (self.name, self.age)


class Son(Father):
    def __init__(self, name, age, collage):
        super().__init__(name, age)
        self.collage = collage

    def __str__(self):
        return "%s的年龄是: %d,他的学历是: %s" % (self.name, self.age, self.collage)


father = Father("父亲", 50)
print(father)

son = Son("儿子", 18, "大学")
print(son)

运行结果:

父亲的年龄是: 50
儿子的年龄是: 18,他的学历是: 大学

示例三:

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

    def __str__(self):
        return "%s的年龄是:%d" % (self.name, self.age)


class Son(Father):
    def __init__(self, name, age, collage):
        super().__init__(name, age)
        self.collage = collage

    def __str__(self):
        return "%s的年龄是:%d,他的学历是:%s" % (self.name, self.age, self.collage)


class GrandChild(Son):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        print("----这里模拟其要做的额外事情....----")


father = Father("父亲", 50)
print(father)

son = Son("儿子", 18, "大学")
print(son)

grandchild = GrandChild("孙子", 1, "未上学")
print(grandchild)

运行结果:

父亲的年龄是:50
儿子的年龄是:18,他的学历是:大学
----这里模拟其要做的额外事情....----
孙子的年龄是:1,他的学历是:未上学
简单总结
  • 如果想要在子类方法中调用被重写的父类方法就可以使用super().父类方法名()

多态

面向对象的特征说明

面向对象编程有3个特征:

  • 封装
  • 继承
  • 多态

封装与继承之前我们已经研究过了,本节课研究多态

多态的概念

多态从字面意思来看,就是多种形态的意思。

在python中它的实际功能是:

  • 如果一个变量存储了某一个实例对象的引用,且通过这个变量调用指向的对象中的某个方法,此时如果变量指向的对象是子类创建的那么就调用子类中的方法,如果是父类创建的对象那么就调用父类的方法
代码示例
class Dog(object):
    def bark(self):
        print("狗汪汪叫...")


class LangDog(Dog):
    def bark(self):
        print("狼狗震耳欲聋的叫...")


class ZangAo(Dog):
    pass


class Person(object):
    def pk_dog(self, dog):
        print("人用力的向狗进行了攻击...")
        dog.bark()


anna = Person()
dog1 = Dog()
dog2 = LangDog()
dog3 = ZangAo()

anna.pk_dog(dog1)
anna.pk_dog(dog2)
anna.pk_dog(dog3)

运行效果:

人 用力的向狗进行了攻击...
狗汪汪叫...
人 用力的向狗进行了攻击...
狼狗震耳欲聋的叫...
人 用力的向狗进行了攻击...
狗汪汪叫...
简单总结

想要实现多态,需要的条件如下:

  1. 有继承
  2. 有重写

静态方法

引入

默认情况下,python类中定义的方法是实例方法,即这个方法有一个默认的形参self,这个self会在方法被调用的时候指向对象

但是有些时候,我们并不需要对象的引用,即self没用,那该怎么处理呢?能不写self吗?

答:可以

静态方法的概念

如果一个方法不写self即不需要实例对象的引用,此时在定义方法的时候可以用@staticmethod对函数进行修饰,被修饰的函数就可以不写self

一句话:被@staticmethod修饰的方法,就是静态方法

代码示例
class Calculator(object):
    """计算器类"""

    def __init__(self):
        # 定义2个默认值
        self.num1 = 0
        self.num2 = 0

    @staticmethod
    def show_menu():
        """因为打印菜单功能方法并不需要self指向的对象,所以就考虑使用静态方法"""
        print("    双双牌计算机 V2022.10")
        print("1. 加法")
        print("2. 减法")
        print("3. 乘法")
        print("4. 除法")
        print("5. 退出")

    def get_nums(self):
        self.num1 = int(input("请输入第1个数:"))
        self.num2 = int(input("请输入第2个数:"))

    def add(self):
        print(self.num1 + self.num2)

    def min(self):
        print(self.num1 - self.num2)

    def mul(self):
        print(self.num1 * self.num2)

    def div(self):
        print(self.num1 / self.num2)

    def run(self):
        while True:
            self.show_menu()
            op = input("请输入要进行的操作:")
            if op == "1":
                self.get_nums()
                self.add()
            elif op == "2":
                self.get_nums()
                self.min()
            elif op == "3":
                self.get_nums()
                self.mul()
            elif op == "4":
                self.get_nums()
                self.div()
            elif op == "5":
                break


# 创建一个计算器对象
cal = Calculator()
# 调用计算器的运行方法
cal.run()

运行效果:

    双双牌计算机 V2022.10
1. 加法
2. 减法
3. 乘法
4. 除法
5. 退出
请输入要进行的操作:
简单总结

如果不需要用到对象,那么就可以将方法用@staticmethod进行修饰,如此一来此方法就变成了静态方法。

类属性

引入

默认情况下 ,当通过同一个类创建了多个实例对象之后,每个实例对象之间是相互隔离的

但是有时候有些数据需要在多个对象之间共享,此时该怎么办呢?

答:类属性

类属性的概念

想要在多个对象之间共享数据,即一些属性需要在多个对象之间共享,这样的属性就是类属性

那怎样定义类属性呢?格式如下:

class 类名:
    类属性 = ....

即在class内且在def之外定义的变量,就叫做类属性

代码示例
class Tool(object):
    tools_num = 0  # 定义一个类属性,用来存储共享的数据

    def __init__(self, name):
        self.name = name
        Tool.tools_num += 1

    def print_info(self):
        print("工具的总数为:", Tool.tools_num)

    def print_info2():
        print("工具的总数为:", Tool.tools_num)


tieqiao = Tool("铁锹")
chutou = Tool("锄头")
dianciluo = Tool("电磁炉")

print("工具的总数为:", Tool.tools_num)  # 可以直接通过 类名.类属性操作
tieqiao.print_info()  # 可以通过Tool创建的任意实例对象调用方法,在方法中获取
Tool.print_info2()  # 通过类名调用时,可以看到这个方法在pycharm中提示错误

类方法

引入

为了更好的对类属性进行操作,Python中提供了另外一种方法类方法

类方法的概念

之前在学习静态方法的时候我们知道可以在方法的名字前面添加@staticmethod此时这个方法就是静态方法,

与这种添加@的方式很类似,如果想要让一个方法成为类方法我们只需要在这个方法的前面添加@classmethod即可,与此同时需要在方法的第1个形参位置添加cls

class 类名:
    @classmethod
    def 类方法名(cls):
        pass
示例代码
class Tool(object):
    tools_num = 0  # 定义一个类属性,用来存储共享的数据

    def __init__(self, name):
        self.name = name
        Tool.tools_num += 1

    def print_info(self):
        print("工具的总数为:", Tool.tools_num)

    @classmethod
    def print_info2(cls):
        print("工具的总数为:", cls.tools_num)


tieqiao = Tool("铁锹")
chutou = Tool("锄头")
dianciluo = Tool("电磁炉")

tieqiao.print_info()
Tool.print_info2()
tieqiao.print_info2()

运行效果:

工具的总数为: 3
工具的总数为: 3
工具的总数为: 3
简单总结
  • 定义类方法时,前面添加@classmethod
  • 类方法的第1个形参,一般都叫做cls(当然了叫什么名字可以任意,但一般都用cls
  • 调用类方法的时候,可以用实例对象类对象调用,但无论用哪种方式调用,类方法中的cls指向类对象

类对象

引入

之前在学习类属性的时候,我们提到过:类属性是可以在多个实例对象之间共享的属性

那么问题来了,类属性到底存在哪里呢?

答:类对象

类对象的概念

之前我们说到通过class定义的就是类(就是一个要创建的商品的模板),通过类名()创建出来的叫做实例对象

其实,定义的类(即用class定义的类)实际上也是一个对象(试想即使我们把 类称之为模板,模板也不是空的啊,也是需要占用内存的对吗)

定义的类其实就是一个对象,为了能够将这个对象与其创建出来的实例对象进行区分,将这个class定义的类叫做类对象

类对象的作用

我们知道实例对象是类 (即类对象)创建出来的,所以类对象对于实例对象而言是共享的,既然是共享的那么就干脆将实例对象都有的而且不变化的内容存储到 类对象 即可,这样会减少内容的占用

那,哪些东西在类对象中存储呢?

  • 类属性
  • 所有的方法

对你没有看错,除了熟知的类属性之外,类对象中存储了class定义的所有的方法(无论是魔法方法、实例方法、静态方法 、类方法都在类对象中存储),因为方法(即函数)的代码是不变的,变化的仅仅是数据而已。

实例对象怎么用类对象

每个实例对象中都会有1个额外默认的属性__class__,这个属性指向了创建当前对象的模板即类对象,所以当调用实例对象.xxx()时,实际上实例对象.__class__.xxx()

dir() 方法

既然我们知道了实例对象中有默认的__class__,那除了它之外还有哪些呢?怎么查看呢?

dir(实例对象)

例如:

['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__']

这么多的__开始的属性,用到什么我们就研究 什么,不用现在立刻研究。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值