Python类和对象

1 面向过程和面向对象

1.1 面向过程和面向对象

  • 面向过程
    面向过程的程序设计把计算机程序视为一系列的命令集合,即一组函数的顺序执行。为了简化程序设计,面向过程把函数继续切分为子函数,即把大块函数通过切割成小块函数来降低系统的复杂度。
    简单来说,就是分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候一个一个依次调用就可以了。
  • 面向对象
    而面向对象的程序设计把计算机程序视为一组对象的集合,而每个对象都可以接收其他对象发过来的消息,并处理这些消息,计算机程序的执行就是一系列消息在各个对象之间传递。
    把构成问题事务分解成各个对象,建立对象的目的不是为了完成一个步骤,而是为了描叙某个事物在整个解决问题的步骤中的行为。

例如五子棋

  • 面向过程的设计思路
    首先分析问题的步骤:1、开始游戏,2、黑子先走,3、绘制画面,4、判断输赢,5、轮到白子,6、绘制画面,7、判断输赢,8、返回步骤2,9、输出最后结果。把上面每个步骤用分别的函数来实现,问题就解决了。
  • 面向对象的设计
    面向对象的设计则是从另外的思路来解决问题。整个五子棋可以分为 1、黑白双方,这两方的行为是一模一样的,2、棋盘系统,负责绘制画面,3、规则系统,负责判定诸如犯规、输赢等。第一类对象(玩家对象)负责接受用户输入,并告知第二类对象(棋盘对象)棋子布局的变化,棋盘对象接收到了棋子的i变化就要负责在屏幕上面显示出这种变化,同时利用第三类对象(规则系统)来对棋局进行判定。

1.2 面向对象程序设计

面向对象编程——Object Oriented Programming,简称OOP,是一种程序设计思想。OOP把对象作为程序的基本单元,一个对象包含了数据和操作数据的函数。

2 基本概念

2.1 对象

对于对象而言,其具有属性与行为。在我们进行描述的时候,属性,多体现为名词,而行为,多体现为动词。

2.2 类

所谓的类,其实指的就是一个类别,即具有相同属性与行为的所有对象构成的一个整体。相同类别的对象之间总会比不同类别的对象之间更加相似。

2.3 类与对象的关系

关于类与对象,我们可以这样总结二者的关系:

  • 类是对象的抽象,而对象是类的具体表现形式。
  • 类是设计的蓝图(模板),而对象就是该蓝图设计出的具体产物。

3 类的定义

3.1 类的定义

我们通过class关键字来定义一个类,格式如下:

	# 类名
class Student(object):
	# 类体
    pass

3.2 动态增加属性与方法

我们要设计一辆汽车,该汽车具有的功能,就会体现在设计图纸中。当我们使用该图纸创建出真正的汽车之后,该汽车自然也就具有设计图纸中所表明的功能。

同样,我们在类的内部定义属性与方法(设计图纸),当我们使用该类创建具体的对象时,该对象也就自然具有类中定义的属性与方法。

除了在类中预先定义属性与方法外,我们也可以动态的为对象增加属性与方法。

  • 动态为类增加属性与方法。
  • 动态增加的方法在调用时,需要显式的传递当前对象。
  • 动态增加的属性与方法仅对当前对象有效,对于其他对象是无效的。

3.3 定义类的属性和方法

对象的行为,我们通过在类内定义方法来实现。所谓方法,其形式与函数非常相似,只不过是定义类的内部,关联了某个对象而已。

一个人的类别,具有走路与跑步两种行为。

3.3.1 self

self代表类的实例,而非类
类的方法与普通的函数只有一个特别的区别——它们必须有一个额外的第一个参数名称, 按照惯例它的名称是 self

该参数用来表示当前的对象。当前对象指的就是调用该方法的时候,所使用的对象。简单来说,就是谁调用了这个方法,当前对象就是谁。

我们知道,如果仅仅定义了类,而没有为类具体化(创建对象),是不能够使用类中定义的功能的,这就正如我们仅有汽车的设计图纸,而没有创建出具体的汽车,就不能够开车出行一样。因此,如果没有创建对象,就不能够调用walk与run方法。而当我们通过对象调用方法时,该对象就会隐式的传递给方法的第一个参数(无需我们显式传递)。

刚才我们在类中定义了方法,来表示对象的行为,但是,对象也是可以具有属性的。例如,人可以具有姓名,年龄等属性。

3.3.2 init

关于定义属性,我们可以在__init__中进行定义。__init__方法是一个特殊的方法,该方法会在创建对象时自动得到执行。并且,每创建一个对象,该方法都会执行一次。

  • 验证__init__方法

既然__init__方法具有在创建对象时自动执行的特征,我们就可以在该方法中执行对象的初始化,定义属性就是一件最常用的操作。而__init__方法具有初始化的能力,我们也习惯将其称为构造器或构造方法。

我们在__init__方法中,定义了name与age两个属性。在创建对象时,就能够自动调用该方法,为对象增加这两个属性。同时,我们也修改了walk与run两个方法的实现,在方法中访问name属性,注意,name与age两个属性都是关联当前self对象的,因此也必须要通过对象才能够访问这两个属性。

  • 含有参数的__init__方法

上述的程序,成功在类中定义了属性与方法,并且在创建对象时,也访问了类中定义的属性与方法。不过,这个程序有一个不足,就是无论我们创建多少个对象,对象的属性都是完全一致的。

为了能够更加灵活的创建对象,让对象具有不同的属性值,我们可以在__init__方法中增加参数,然后在创建对象时,动态传递实际参数来初始化对象的属性(不再使用固定值初始化),这样就能够避免创建属性值完全相同的对象了。

  • 通过__init__方法初始化。
# class后面紧跟着类名,即student 雷鸣通常是大写开头的单词,紧接着(object),表示该类是从哪个类继承下来的
# 如果没有合适的继承类,就使用object类,这是所有类最终都会继承的类
class Student(object):
    # 类的初始化,提供class和对象的接口
    def __init__(self,name,age):
        # 添加两个属性
        # self.name是属性,name是实例对象
        self.name=name
        self.age=age
    # 类里面可以定义属性还可以定义方法
    def run(self):
        return "我是人,我会跑"

# 定义好了类,就可以根据类创建student实例
stu1 = Student("wang",18)
print(stu1)
# 变量stu1指向的就是一个student实例
print(type(stu1))
# 调用属性
print(stu1.age,stu1.name)
# 调用方法
print(stu1.age,stu1.name,stu1.run())

4 面向对象三大特征

4.1 封装

在上面的Student类中,每个实例就拥有各自的name和score这些数据。我们可以通过函数来访问这些数据,比如打印一个学生的成绩

def print_score(std):
	print('%s: %s' % (std.name, std.score))

但是,既然Student实例本身就拥有这些数据,要访问这些数据,就没有必要从外面的函数去访问,可以直接在Student类的内部定义访问数据的函数,这样,就把“数据”给封装起来了。这些封装数据的函数是和Student类本身是关联起来的,我们称之为类的方法

class Student(object):
    # 类的初始化,提供class和对象的接口
    # 用init将属性绑定
    def __init__(self,name,age,score):
        # 添加两个属性
        # self.name是属性,name是实例对象
        self.name=name
        self.age=age
        self.score=score 
    # 封装
    # 直接在Student类的内部定义访问数据的函数
    def get_message(self):
        return "姓名:%s,年龄:%s,成绩:%s"%(self.name,self.age,self.score)
    # 给Student类增加新的方法
    # get_grade方法可以直接在实例变量上调用,外部不需要知道内部实现细节
    def get_grage(self):
        if self.score>90:
            return "优秀"
        if self.score>60 and self.score<90:
            return "及格"
        if self.score<60:
            return "不及格"
        else:
            return "成绩有问题"

print(stu1.get_message())       # 姓名:wang,年龄:18,成绩:95
print(stu1.get_grage())         # 优秀

我们从外部看Student类,就只需要知道,创建实例需要给出name、age和score,而如何打印,都是在Student类的内部定义的,这些数据和逻辑被“封装”起来了,调用很容易,但却不用知道内部实现的细节。

小结

  • 类是创建实例的模板,而实例则是一个一个具体的对象,各个实例拥有的数据都互相独立,互不影响;
  • 方法就是与实例绑定的函数,和普通函数不同,方法可以直接访问实例的数据;
  • 通过在实例上调用方法,我们就直接操作了对象内部的数据,但无需知道方法内部的实现细节。
  • 和静态语言不同,Python允许对实例变量绑定任何数据,也就是说,对于两个实例变量,虽然它们都是同一个类的不同实例,但拥有的变量名称都可能不同
4.1.1 封装的优势
  • 私有的意义

通过调整之后,我们能够在类外对私有变量(方法)进行访问,可以这么做,可能要为每一个私有属性(方法)都配上两个公有的方法,这岂不是过于的繁琐吗?我们之前非私有的方式岂不是更加简便?

  • 私有成员的假象

不过,在Python语言中,所谓的私有,不过是一种假象。当我们在类中定义私有成员时,在程序内部会将其处理成_类名 + 原有成员名称的形式。也就是会将私有成员的名字进行一下伪装而已,如果我们使用处理之后的名字,还是能够进行访问的。但是,我们不要这样做,因为这会破坏封装性,从而给自己埋下一颗不定时的炸弹。

程序:私有的伪装

4.1.2 访问限制

在Class内部,可以有属性和方法,而外部代码可以通过直接调用实例变量的方法来操作数据,这样,就隐藏了内部的复杂逻辑。
如果要让内部属性不被外部访问,可以把属性的名称前加上两个下划线__,在Python中,实例的变量名如果以__开头,就变成了一个私有变量(private),只有内部可以访问,外部不能访问

class Student(object):

    def __init__(self, name, score):
        self.__name = name
        self.__score = score

    def print_score(self):
        print('%s: %s' % (self.__name, self.__score))

stu1 = Student('wang', 59)
stu1.__name
# 对于外部代码来说,没什么变动,但是已经无法从外部访问实例变量.__name和实例变量.__score了
# AttributeError: 'Student' object has no attribute '__name'

这样就确保了外部代码不能随意修改对象内部的状态,这样通过访问限制的保护,代码更加健壮。

但是如果外部代码要获取name和score,可以给Student类增加get_name和get_score这样的方法
允许外部代码修改score,可以再给Student类增加set_score方法

# 为了限制score的范围,可以通过一个set_score()方法来设置成绩
# 通过一个get_score()来获取成绩,这样,在set_score()方法里,就可以检查参数
class Stu(object):
    def get_score(self):
        return self.score
    def set_score(self,value):
        if not isinstance(value,int):
            # raise 表示对程序报错时指定报错输出的错误内容
            # 如果数据类型不是int则会报错
            raise ValueError("typeError")
        elif value > 100 or value < 0:
            # 如果超过范围则要报错
            raise ValueError("numError")
        self.score = value
s = Stu()
s.set_score(100)
print(s.get_score())        # 100
# 现在,对任意的Student实例进行操作,就不能随心所欲地设置score了
s.set_score(200)
print(s.get_score())        # numError
s.set_score("yibai")
print(s.get_score())        # typeError
  • print 和 raise的区别
    raise这个方法是在执行出错时能标出来是在哪里出了错,而且之后的语句都不会在执行

4.2 继承

在OOP程序设计中,当我们定义一个class的时候,可以从某个现有的class继承,新的class称为子类(Subclass),而被继承的class称为基类、父类或超类(Base class、Super class)。

# 继承
# 父类 Animal
# 现在已经编写了一个名为Animal的class,有一个print_animal()方法可以直接打印
class Animal(object):
    def print_animal(self):
        return "我是小动物,我会跑"
ani = Animal()
print(ani.print_animal())       # 我是小动物,我会跑

# 定义一个Dog类
# 对于Dog来说,Animal就是它的父类,对于Animal来说,Dog就是它的子类
class Dog(Animal):
    pass
dog = Dog()
# 本身Dog这个类是没有方法的,但是因为它继承了父类Animal,所以就有了父类所有的方法
print(dog.print_animal())       # 我是小动物,我会跑

# 对于Cat来说,Animal就是它的父类,对于Animal来说,Cat就是它的子类
# cat和dog两个类类似
# 子类继承父类方法的同时还可以定义自己的方法
class Cat(Animal):
    def miao(self):
        return "喵喵喵"
cat = Cat()
print(cat.print_animal(),cat.miao())     # 我是小动物,我会跑 喵喵喵

当子类继承了父类,子类就可以继承父类的成员。然而,父类的成员未必完全适合于子类,比如小动物会跑,二鸟会飞,此时,子类就将父类中的成员进行调整,以实现适合子类的特征与功能。我们将父类中的成员在子类中重新定义的现象,称为重写。
当通过子类对象访问成员时,如果子类重写了父类的成员,将会访问子类自己的成员。否则(没有重写)访问父类的成员。

# 如果子类定义的方法和父类重名了,就会屏蔽掉父类的方法
class Bird(Animal):
    def print_animal(self):
        return "我是小动物,我会飞"
bird = Bird()
print(bird.print_animal())      # 我是小动物,我会飞

4.3 多态

# 多态
def print_ani(animal):
    return animal.print_animal()
print(print_ani(Animal()))
print(print_ani(Bird()))
# 我是小动物,我会跑
# 我是小动物,我会飞

class Tiger(Animal):
    def print_animal(self):
        return "Tiger is running"
print(print_ani(Tiger()))
# Tiger is running

新增一个Animal的子类,不必对print_animal()做任何修改,实际上,任何依赖Animal作为参数的函数或者方法都可以不加修改地正常运行,原因就在于多态。
多态的好处就是,当我们需要传入Dog、Cat、Tiger……时,我们只需要接收Animal类型就可以了,因为Dog、Cat、Tiger……都是Animal类型,然后,按照Animal类型进行操作即可。由于Animal类型有print_animal()方法,因此,传入的任意类型,只要是Animal类或者子类,就会自动调用实际类型的print_animal()方法,这就是多态的意思:
对于一个变量,我们只需要知道它是Animal类型,无需确切地知道它的子类型,就可以放心地调用print_animal()方法,而具体调用的print_animal()方法是作用在Animal、Dog、Cat还是Bird对象上,由运行时该对象的确切类型决定,这就是多态真正的威力:调用方只管调用,不管细节,而当我们新增一种Animal的子类时,只要确保run()方法编写正确,不用管原来的代码是如何调用的。这就是著名的开闭原则

  • 对扩展开放:允许新增Animal子类;
  • 对修改封闭:不需要修改依赖Animal类型的print_ani()等函数。
    继承还可以一级一级地继承下来,就好比从爷爷到爸爸、再到儿子这样的关系。而任何类,最终都可以追溯到根类object,这些继承关系看上去就像一颗倒着的树。
Object
Animal
Cat
Dog
Bird
Tiger

5 获取对象信息

5.1 type()

首先,我们来判断对象类型,使用type()函数:
基本类型都可以用type()判断:

print(type(123))
# <class 'int'>

print(type('str'))
# <class 'str'>

print(type(None))
# <class 'NoneType'>

# 如果一个变量指向函数或者类,也可以用type()判断
print(type(abs))
# <class 'builtin_function_or_method'>

print(type(print_ani))
# <class 'function'>

# 比较两个变量的type类型是否相同
print(type(123)==type(456))
# True

print(type(123)==int)
# True

print(type("abc")==type("sss"))
# True

print(type("abc")==int)
# False

# 判断一个对象是否是函数可以使用types模块中定义的常量
print(type(abs)==types.BuiltinFunctionType)
# True

print(type(lambda x: x)==types.LambdaType)
# True

print(type((x for x in range(10)))==types.GeneratorType)
# True

import types
def fn():
    pass
print(type(fn)==types.FunctionType)
# True

5.2 isinstance()

对于class的继承关系来说,使用type()就很不方便。我们要判断class的类型,可以使用isinstance()函数。
我们回顾上次的例子,如果继承关系是:

Object
Animal
Dog
Husky
# 根据继承关系建立Animal、Dog、Husky类
class Animal:
    pass
class Dog(Animal):
    pass
class Husky(Dog):
    pass
a = Animal()
d = Dog()
h = Husky()
# 因为h变量指向的就是Husky对象
print(isinstance(h, Husky))
# True
# h虽然自身是Husky类型,但由于Husky是从Dog继承下来的,所以,h也还是Dog类型。
# 换句话说,isinstance()判断的是一个对象是否是该类型本身,或者位于该类型的父继承链上
print(isinstance(h, Dog))
# True
print(isinstance(h, Animal))
# True
print(isinstance(d, Husky))
# False

# 能用type()判断的基本类型也可以用isinstance()判断
print(isinstance('a', str))
# True
print(isinstance(123, int))
# True
# 判断一个变量是否是某些类型中的一种,比如下面的代码就可以判断是否是list或者tuple
print(isinstance([1, 2, 3], (list, tuple)))
# True

6 类的成员

6.1 类属性与实例属性

之前我们定义的属性,都是与类的对象相关联的,这种属性称为实例属性。对于对象,也称为实例,例如,创建类的对象,也可以称为,创建类的实例。

除了实例属性外,我们也可以定义类属性,所谓类属性,就是与当前类进行关联,不属于对象。类属性直接定义在类体中,例如:

# 根据类创建的实例可以任意绑定属性。
# 给实例绑定属性的方法是通过实例变量,或者通过self变量
class Student(object):
    def __init__(self, name):
        self.name = name

s = Student("tom")

# 如果Student类本身需要绑定一个属性,可以直接在class中定义属性,这种属性是类属性,归Student类所有
# 当我们定义了一个类属性后,这个属性虽然归类所有,但类的所有实例都可以访问到
class Student(object):
    name = "Student"

s = Student()   # 创建实例s
print(s.name)   # 打印name属性,因为实例并没有name属性,所以会继续查找class的name属性
# Student
print(Student.name) # 打印类的name属性
# Student
s.name = "tom"
print(s.name) # 由于实例属性优先级比类属性高,因此,它会屏蔽掉类的name属性
# tom
print(Student.name) # 但是类属性并未消失,用Student.name仍然可以访问
# Student
del s.name # 如果删除实例的name属性
print(s.name) # 再次调用s.name,由于实例的name属性没有找到,类的name属性就显示出来了
# Student

6.2 类属性与实例属性的区别

  • 属性的绑定不同
    类属性与类进行绑定,与类的任何一个对象都无关(但是可以共享给所有对象去访问)。实例属性与对象进行绑定,每个对象都有自己的实例属性。如果一个对象改变了实例属性的值,对其他对象没有影响。

  • 访问方式不同
    类属性既可以通过类访问,也可以通过对象访问(类属性会共享给所有对象)。而实例属性只能通过对象访问,而不能通过类访问(实例属性只属于某个类的对象,与类本身无关)。在通过对象访问类属性时,仅限于只读访问,而无法修改类属性的值。因为尝试进行的修改,只不过是动态的为当前对象新增一个实例属性(与类属性同名)而已。

6.3 类方法与实例方法对属性的访问

在类方法中,需要通过参数(cls)访问类属性,而不能直接访问类属性。实例方法没有cls参数,可以通过类名来访问类属性,但是,这是一种硬编码的方式,如果以后类名发生了改变,则我们通过类名访问类属性的语句都需要进行修改。这不利于程序的维护。为了降低耦合性,我们在实例方法中,可以通过__class__来动态获取当前对象所属的类:

# self.__class__ # 相当于获取到了cls,也就是self对象所属的类(动态获取)
class Student(object):
    def __init__(self,name):
        self.name = name
stu = Student("tom")
print(type(stu),type(Student))
      # stu的类属性    Student的类属性
print(stu.__class__, Student.__class__)

# 结果如下:
<class '__main__.Student'> <class 'type'>
<class '__main__.Student'> <class 'type'>

更简单一些,如果是在类的内部,我们还可以省略对象的限定,直接使用__class__

说明:

  • 类变量可以通过类直接访问
  • 类变量可以通过类的实例直接访问
  • 类变量可以通过此类的对象的 __class__属性间接访问
# 此示例示意类变量的用法,及类和对象的关系
class Student(object):
    total_count = 0  # 类变量,此变量用来记录所有对象的个数
    def __init__(self, name):
        self.name = name
 
print("Student类内的类变量Student.total_count=",Student.total_count)
 
# __class__的用法
# 类变量可以通过类直接访问
Student.total_count += 1
print("Student.total_count=", Student.total_count)
 
# 类变量可以通过类的实例直接访问
stu1 = Student('小张')
print("stu1.total_count=", stu1.total_count)
stu1.total_count = 100  # 此做法是为实例添加一个变量,并不是修改类变量
print('Student.total_count=', Student.total_count)  # 1
 
# 类变量可以通过此类的对象的 __class__属性间接访问
stu1.__class__.total_count = 200
print("Student.total_count=", Student.total_count)  # 200

这种访问的优势在于,以后就算类名发生了改变,访问类属性的语句也无需进行任何修改,利于程序的维护。

class Student:
    total_count = 0  # 类变量,此变量用来记录所有对象的个数
    def __init__(self, name):
        self.name = name
        # 如果一个对象诞生,则将类变量的total_count做+1操作
        # 来记录当前对象的个数
        self.__class__.total_count += 1
    def __del__(self):
        self.__class__.total_count -= 1
 
print("当前有%d个Student的实例对象" % Student.total_count)
stu1 = Student('小张')
print("当前有%d个Student的实例对象" % Student.total_count)
stu2 = Student('小李')
print("当前有%d个Student的实例对象" % Student.total_count)
del stu1        # 当对象销毁时。自动将类变量做减1操作
print("当前有%d个Student的实例对象" % Student.total_count)

class Student(object):
    def __init__(self,name):
        self.name = name
stu = Student("tom")
print(type(stu),type(Student))
      # stu的类属性    Student的类属性
print(stu.__class__, Student.__class__)

# 结果如下:
当前有0个Student的实例对象
当前有1个Student的实例对象
当前有2个Student的实例对象
当前有1个Student的实例对象
<class '__main__.Student'> <class 'type'>
<class '__main__.Student'> <class 'type'>

7__slots__

如果我们想要限制实例的属性,比如,只允许对Student实例添加name和age属性。
为了达到限制的目的,Python允许在定义class的时候,定义一个特殊的__slots__变量,来限制该class实例能添加的属性

class Stu(object):
    __slots__=("name","age","sex")
s = Stu()
s.name="li"
s.age=18
s.sex="boy"
#s.score=20      # 错误,因为slots方法里没有score这一项
print(s.name,s.age,s.sex)

使用__slots__要注意,__slots__定义的属性仅对当前类实例起作用,对继承的子类是不起作用的
当子类同时设定了__slots__这个方法后,那么子类同时拥有自身以及父类的这个__slots__这个方法

class Stu1(Stu):
    __slots__=("score")
    # 现在Stu1就有了以下方法:name age sex score
stu1=Stu1()
stu1.name="li"
stu1.age=18
s.sex="boy"
stu1.score=90
print(stu1.age,stu1.score)

8 魔法方法

在Python语言中,定义了一些特殊的方法,这些方法通常不会显式去调用,而是在特定的场合下隐式调用执行的。这些特殊的方法名称都是以双下划线开头,并且以双下划线结尾,我们将这些方法称为魔法方法。

__new__(cls, ……)
创建cls类的对象。通过参数的cls可知,该方法是一个类方法,但是不需要使用装饰器来修饰(@classmethod),该方法的其他参数由构造器传入。该方法中,通常会调用父类的__new__方法来创建对象,并返回。如果该方法返回值为当前类的对象,则会继续调用初始化方法(__init__),传入当前创建的对象,否则,初始化方法不会执行。

该方法通常不需要自行实现,当我们实现该方法时,主要用来完成自定义的对象创建。

__init__(self)
初始化方法,在创建对象时,如果__new__方法创建并返回了当前类型的对象,则会调用该方法,对new创建的对象执行初始化。

__del__(self)
当销毁对象时,调用该方法。

__str__(self)
当调用内建函数str,format或print时,就会调用对象的该方法。该方法必须返回字符串(str)类型,用来描述对象的字符串表示。

__repr__(self)
当调用内建函数repr时,就会调用对象的该方法。该方法必须返回字符串(str)类型,用来描述对象的字符串表示。如果类中定义了该方法,但是没有定义__str__方法,则当需要调用__str__方法时,也会调用该方法代替。

__bytes__(self)
当调用内建函数bytes时,会调用该方法,该方法必须返回字节(bytes)类型,用来以字节的形式描述对象。

__call__(self)
当把类的对象作为方法调用时,就会调用该方法。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值