练习40--模块、类和对象——类和对象详解1
一 类和对象详解
类和对象的基本概念见上一个博客:https://www.cnblogs.com/luoxun/p/13383993.html
- 面向对象是一种编程方式,此编程方式的实现是基于对 类 和 对象 的使用
- 类 是一个模板,模板中包装了多个“函数”供使用(可以讲多函数中公用的变量封装到对象中)
- 对象,根据模板创建的实例(即:对象),实例用于调用被包装在类中的函数
- 面向对象三大特性:封装、继承和多态
1 python程序中使用类的顺序
- 创建(定义)类,也就是制作图纸的过程;
- 创建类的实例对象(根据图纸造出实际的物品),通过实例对象实现特定的功能。
2 类的定义——使用class关键字实现
- 基本概念:类就是一种对象类型,和跟前面学习过的数值、字符串、列表等等类型一样。
- 基本语法:
-
1 class 类名: 2 """类帮助信息""" # 类文档字符串 3 多个(≥0)类属性... # 类体 4 多个(≥0)类方法...
- 注意:
- 类属性指的就是包含在类中的变量;而类方法指的是包含类中的函数。换句话说,类属性和类方法其实分别是包含类中的变量和函数的别称。
- 同属一个类的所有类属性和类方法,要保持统一的缩进格式,通常统一缩进 4 个空格。
- 无论是类属性还是类方法,对于类来说,它们都不是必需的,可以有也可以没有
- Python 类中属性和方法所在的位置是任意的,即它们之间并没有固定的前后次序。
- 命名:
- 最好使用能代表该类功能的单词,首字母大写
- 如果必要,可以使用多个单词组合而成,建议每个单词的首字母大写,其它字母小写。
- 说明文档:
- 和函数一样,我们也可以为类定义说明文档,其要放到类头之后,类体之前的位置
- 可通过类名.__doc__查看
3 python__init__()类构造方法
- 功能:用于创建对象时使用,每当创建一个类的实例对象时,Python 解释器都会自动调用它。
- 语法:
-
1 def __init__(self,...): 2 代码块
- 注意,此方法的方法名中,开头和结尾各有 2 个下划线,且中间不能有空格。
- 特点:
- __init__() 方法必须包含一个名为 self 的参数,且必须作为第一个参数,还有,在创建类对象时,无需给 self 传参即可。
- __init__() 构造方法中,除了 self 参数外,还可以自定义一些参数,参数之间使用逗号“,”进行分割。创建对象时调用类的构造方法的过程中,需要手动传递参数。
- self 代表类的实例,self 在定义类的方法时是必须有的,虽然在调用时不必传入相应的参数。
- 注意:
- 每个类必须有一个构造函数,即使它只依赖于默认构造函数。
- 当创建没有构造函数的类时,Python会自动创建一个不执行任何操作的默认构造函数。
- 当我们想要把类中需要自动绑定的一些属性通过构造函数来初始化实现时,我们会显示定义类中的构造函数。
- 只要实例化一个类(创建对象),构造函数就被会自动调用,不管它是人为显式定义的,还是调用的默认的空的构造函数
4 python的self参数
- 由来:Python 规定,无论是构造方法还是实例方法,最少要包含一个参数,并没有规定该参数的具体名称,将其命名为 self,只是程序员之间约定俗成的一种习惯。
- 功能:Python 会自动绑定类方法的第一个参数指向调用该方法的对象。即同一个类可以产生多个对象,当某个对象调用类方法时,该对象会把自身的引用作为第一个参数自动传给该方法
- 简单来说:self代表类的实例,而非类
- 例如:
-
1 class Test: 2 def prt(self): 3 print(self) 4 print(self.__class__) 5 6 t = Test() 7 t.prt()
- 执行结果:
-
1 <__main__.Test instance at 0x100771878> 2 __main__.Test
- 从执行结果可看出:self 代表的是类的实例,代表当前对象的地址,而 self.class 则指向类。
5 python类对象的创建和使用
(1)python类的实例化
- 定义:创建类对象的过程,类的实例化类似函数调用方式。
- 语法:
类名(参数)
- 举例:
-
1 class dog(object): 2 "dog class" 3 4 def __init__(self,name): #构造方法,不写调用默认的构造方法 5 self.name = name 6 7 def eat(self,food): 8 print("the dog name is {0},it like food is {1}".format(self.name,food)) 9 10 d = dog("alex") 11 d.eat("hotdog") 12 #输出 13 the dog name is alex,it like food is hotdog
- 分析:
- 总结:
- 通过实例化语句调用类(就像调用函数一样),在内存中开辟一块空间,为其命名
- 把对象的地址和参数传入构造函数,实现必要属性的绑定
- 返回创建的对象(将各个属性保存到对象中)
(2)python类对象的使用
1 class CLanguage : 2 # 下面定义了2个类变量 3 name = "C语言中文网" 4 add = "http://c.biancheng.net" 5 def __init__(self,name,add): 6 #下面定义 2 个实例变量 7 self.name = name 8 self.add = add 9 print(name,"网址为:",add) 10 # 下面定义了一个say实例方法 11 def say(self, content): 12 print(content) 13 # 将该CLanguage对象赋给clanguage变量 14 clanguage = CLanguage("C语言中文网","http://c.biancheng.net")
- 类对象访问变量或方法
- 访问变量:类对象名.变量名 clanguage.name="Python教程"
- 访问方法:对象名.方法名(参数) clanguage.say("人生苦短,我用Python")
- 对象名和变量名以及方法名之间用点 "." 连接。
- 给类对象动态添加/删除变量
- Python 支持为已创建好的对象动态增加实例变量,通过直接增加一个新的实例变量并为其赋值即可添加:eg:clanguage.money= 159.9
- 使用 del 语句即可动态删除实例变量,eg:del clanguage.money
- 给类对象动态添加方法
- 手动传入第一个参数值:
-
1 # 先定义一个函数 2 def info(self): 3 print("---info函数---", self) 4 # 使用info对clanguage的foo方法赋值(动态绑定方法) 5 clanguage.foo = info 6 # Python不会自动将调用者绑定到第一个参数, 7 # 因此程序需要手动将调用者绑定为第一个参数 8 clanguage.foo(clanguage) # ① 9 10 # 使用lambda表达式为clanguage对象的bar方法赋值(动态绑定方法) 11 clanguage.bar = lambda self: print('--lambda表达式--', self) 12 clanguage.bar(clanguage) # ②
- 上面的第 5 行和第 11 行代码分别使用函数、lambda 表达式为 clanguage 对象动态增加了方法,但对于动态增加的方法,Python 不会自动将方法调用者绑定到它们的第一个参数,因此程序必须手动为第一个参数传入参数值,如上面程序中 ① 号、② 号代码所示。
-
- 借助 types 模块下的 MethodType方法
-
1 def info(self,content): 2 print("C语言中文网地址为:%s" % content) 3 # 导入MethodType 4 from types import MethodType 5 clanguage.info = MethodType(info, clanguage) 6 # 第一个参数已经绑定了,无需传入 7 clanguage.info("http://c.biancheng.net")
- 可以看到,由于使用 MethodType 包装 info() 函数时,已经将该函数的 self 参数绑定为 clanguage,因此后续再使用 info() 函数时,就不用再给 self 参数绑定值了。
-
- 手动传入第一个参数值:
6 python类属性和实例属性
参考内容:https://www.cnblogs.com/wupeiqi/p/4766801.html
(1)分类
- 类属性:是在类体中定义的变量
- 类体中、所有函数之外:此范围定义的变量,称为类属性或类变量;
- 类体中,所有函数内部:以“self.变量名”的方式定义的变量,称为实例属性或实例变量;
- 类体中,所有函数内部:以“变量名=变量值”的方式定义的变量,称为局部变量。
- 类方法:是在类体中定义的函数
- 用 @classmethod 修饰的方法为类方法;
- 采用 @staticmethod 修饰的方法为静态方法;
- 不用任何修改的方法为实例方法。
- 其中 @classmethod 和 @staticmethod 都是函数装饰器。
- 类的成员:
- 字段(类似后文的类属性):
- 普通字段:就是实例变量
- 静态字段:就是类变量
- 方法(就是类方法):
- 类方法
- 静态方法
- 实例方法
- 属性(没搞清楚):
- 普通属性:
- 字段(类似后文的类属性):
- 类成员的存储空间分析:
- 特点:
- 所有成员中,只有普通字段的内容保存对象中,即:根据此类创建了多少对象,在内存中就有多少个普通字段。
- 而其他的成员,则都是保存在类中,即:无论对象的多少,在内存中只创建一份。
- 举例:
-
1 class Province: 2 # 静态字段 3 country = '中国' 4 5 def __init__(self, name): 6 # 普通字段 7 self.name = name 8 9 # 直接访问普通字段 10 obj = Province('河北省') 11 print obj.name 12 13 # 直接访问静态字段 14 Province.country
-
由上图可知:静态字段在内存中只保存一份,普通字段在每个对象中都要保存一份
-
- 应用场景: 通过类创建对象时,如果每个对象都具有相同的字段,那么就使用静态字段
- 特点:
(2)类属性和实例属性讲解
- 类属性/类变量——静态字段:
- 定义:类变量指的是在类中,但在各个类方法外定义的变量
- 举例:
-
1 class CLanguage : 2 # 下面定义了2个类变量 3 name = "C语言中文网" 4 add = "http://c.biancheng.net" 5 # 下面定义了一个say实例方法 6 def say(self, content): 7 print(content)
-
- 特点:所有类的实例化对象都同时共享类变量,也就是说,类变量在所有实例化对象中是作为公用资源存在的。
- 调用:
- 使用类名直接调用
-
1 #使用类名直接调用 2 print(CLanguage.name) 3 print(CLanguage.add) 4 #修改类变量的值 5 CLanguage.name = "Python教程" 6 CLanguage.add = "http://c.biancheng.net/python" 7 print(CLanguage.name) 8 print(CLanguage.add)
- 通过类名不仅可以调用类变量,也可以修改它的值。
- 通过类名修改类变量,会作用到所有的实例化对象
-
- 使用类的实例化对象调用【不推荐】
-
1 clang = CLanguage() 2 print(clang.name) 3 print(clang.add)
-
- 通过类对象是无法修改类变量的。通过类对象对类变量赋值,其本质将不再是修改类变量的值,而是在给该对象定义新的实例变量。
- 使用类名直接调用
- 添加:
- 除了可以通过类名访问类变量之外,还可以动态地为类和对象添加类变量
-
1 clang = CLanguage() 2 CLanguage.catalog = 13 3 print(clang.catalog)
- 实例变量/实例属性——普通字段
- 定义:实例变量指的是在任意类方法内部,以“self.变量名”的方式定义的变量
- 特点:只作用于调用方法的对象
- 调用:
- 实例变量只能通过对象名访问,无法通过类名访问。
-
1 class CLanguage : 2 def __init__(self): 3 self.name = "C语言中文网" 4 self.add = "http://c.biancheng.net" 5 # 下面定义了一个say实例方法 6 def say(self): 7 self.catalog = 13 8 9 clang = CLanguage() 10 print(clang.name) 11 print(clang.add) 12 #由于 clang 对象未调用 say() 方法,因此其没有 catalog 变量,下面这行代码会报错 13 #print(clang.catalog) 14 15 clang2 = CLanguage() 16 print(clang2.name) 17 print(clang2.add) 18 #只有调用 say(),才会拥有 catalog 实例变量 19 clang2.say() 20 print(clang2.catalog)
此 CLanguage 类中,name、add 以及 catalog 都是实例变量。其中,由于 __init__() 函数在创建类对象时会自动调用,而 say() 方法需要类对象手动调用。因此,CLanguage 类的类对象都会包含 name 和 add 实例变量,而只有调用了 say() 方法的类对象,才包含 catalog 实例变量。
- 通过某个对象修改实例变量的值,不会影响类的其它实例化对象,更不会影响同名的类变量。
- 通过类对象是无法修改类变量的原因:
- 通过类对象修改类变量的值,不是在给“类变量赋值”,而是定义新的实例变量。
-
1 clang = CLanguage() 2 #clang访问类变量 3 print(clang.name) 4 print(clang.add) 5 6 clang.name = "Python教程" 7 clang.add = "http://c.biancheng.net/python" 8 #clang实例变量的值 9 print(clang.name) 10 print(clang.add) 11 #类变量的值 12 print(CLanguage.name) 13 print(CLanguage.add)
- 类中,实例变量和类变量可以同名,但这种情况下使用类对象将无法调用类变量,它会首选实例变量,这也是不推荐“类变量使用对象名调用”的原因。
- 添加:Python 只支持为特定的对象添加实例变量
- 局部变量:
- 定义:局部变量指的是在任意类方法内部,以“变量名=值”的方式定义的变量
- 功能:定义局部变量是为了所在类方法功能的实现。
- 特点:局部变量只能用于所在函数中,函数执行完成后,局部变量也会被销毁。
7 python的实例方法、静态方法和类方法
我们已经了解到:采用 @classmethod 修饰的方法为类方法;采用 @staticmethod 修饰的方法为静态方法;不用任何修改的方法为实例方法。
(1)python类实例方法
- 定义:通常情况下,在类中定义的方法默认都是实例方法。类的构造方法理论上也属于实例方法,只不过它比较特殊。
- 举例:
1 class CLanguage: 2 #类构造方法,也属于实例方法 3 def __init__(self): 4 self.name = "C语言中文网" 5 self.add = "http://c.biancheng.net" 6 # 下面定义了一个say实例方法 7 def say(self): 8 print("正在调用 say() 实例方法")
- 特点:它最少也要包含一个 self 参数,用于绑定调用此方法的实例对象(Python 会自动完成绑定)
- 调用:
- 实例方法通常会用类对象直接调用
- Python 也支持使用类名调用实例方法,但此方式需要手动给 self 参数传值。
- 调用方法分类:http://c.biancheng.net/view/2267.html
- 绑定方法:用类的实例对象访问类成员的方式
- 非绑定方法:用类名调用类成员的方式
(2)python类方法
- 定义:Python 类方法和实例方法相似,它最少也要包含一个参数,只不过类方法中通常将其命名为 cls,Python 会自动将类本身绑定给 cls 参数(注意,绑定的不是类对象)
- 我们在调用类方法时,无需显式为 cls 参数传参。
- 和 self 一样,cls 参数的命名也不是规定的(可以随意命名),只是 Python 程序员约定俗称的习惯而已。
- 特点:和实例方法最大的不同在于,类方法需要使用
@classmethod
修饰符进行修饰 - 举例:
1 class CLanguage: 2 #类构造方法,也属于实例方法 3 def __init__(self): 4 self.name = "C语言中文网" 5 self.add = "http://c.biancheng.net" 6 #下面定义了一个类方法 7 @classmethod 8 def info(cls): 9 print("正在调用类方法",cls) 10 11 #使用类名直接调用类方法 12 CLanguage.info() 13 #使用类对象调用类方法 14 clang = CLanguage() 15 clang.info()
注意,如果没有 @classmethod,则 Python 解释器会将 fly() 方法认定为实例方法,而不是类方法。
- 调用:
- 推荐使用类名直接调用
- 当然也可以使用实例对象来调用(不推荐)
(3)python类的静态方法
- 定义:静态方法,其实就是我们学过的函数,和函数唯一的区别是,静态方法定义在类这个空间(类命名空间)中,而函数则定义在程序所在的空间(全局命名空间)中。静态方法需要使用
@staticmethod
修饰 - 特点:静态方法没有类似 self、cls 这样的特殊参数,因此 Python 解释器不会对它包含的参数做任何类或对象的绑定。也正因为如此,类的静态方法中无法调用任何类属性和类方法。
- 调用:既可以使用类名,也可以使用类对象。
- 举例:
1 class CLanguage: 2 @staticmethod 3 def info(name,add): 4 print(name,add) 5 6 #使用类名直接调用静态方法 7 CLanguage.info("C语言中文网","http://c.biancheng.net") 8 #使用类对象调用静态方法 9 clang = CLanguage() 10 clang.info("Python教程","http://c.biancheng.net/python")
在实际编程中,几乎不会用到类方法和静态方法,因为我们完全可以使用函数代替它们实现想要的功能,但在一些特殊的场景中(例如工厂模式中),使用类方法和静态方法也是很不错的选择。