在之前关于面向对象的编程中,只是简单的介绍。先对该部分进行扩展。
在python2版本中,会发现__metaclass__ = type这样的开头,这表示将类定义为新式类。关于新式类与旧式类的区别可查看博文(https://blog.csdn.net/mrsun1/article/details/105569071)。
1、构造函数
构造函数用于创建类时的一些初始化过程,python的构造函数为__init__(self),如下所示
# 构造函数__init__()
class ClassTest1:
def __init__(self):
self.name = "tom"
classTest1 = ClassTest1()
print(classTest1.name)
结果
tom
构造函数也可以带形参
# 带参数的构造函数__init__(),__del__()析构函数用于对象销毁时调用
class ClassTest2:
def __init__(self, value="tony"):
self.name = value
classTest2 = ClassTest2(value="james")
print(classTest2.name)
结果
james
如果是两个类存在继承关系呢?
#继承关系下的构造函数调用
class ClassTest3:
def __init__(self):
self.name = "tom"
def method(self):
print(self.name)
class ClassTest4(ClassTest3):
def __init__(self):
pass
classTest4 = ClassTest4()
# 该种方式会出现错误,因为ClassTest4中没有属性name,但是其继承了基类的method方法
# 在该方法中使用到了name属性,所以报错。这也是因为在初始化ClassTest4是没有调用ClassTest3的
# 构造函数导致的
classTest4.method()
可以使用super或直接调用基类的构造函数执行基类的构造函数
class ClassTest3:
def __init__(self):
self.name = "tom"
def method(self):
print(self.name)
class ClassTest4(ClassTest3):
def __init__(self):
# 以前的方式调用基类的构造函数
# ClassTest3.__init__(self)
# 新的方式调用基类的构造函数
# 使用super的好处在于可以直接调用其所有基类的构造函数
super().__init__()
pass
classTest4 = ClassTest4()
classTest4.method()
结果
tom
2、多态
多态是指同一行为具有不同的表现形式。
# 测试多态
class Dog:
def __init__(self, name):
self.name = name
def run(self):
print(self.name + " run")
class BlackDog(Dog):
def run(self):
print(self.name + "run")
class WhiteDog(Dog):
def run(self):
print(self.name + "run")
class Person:
def __init__(self, name):
self.name = name
def run(self, dog):
print(self.name + " with " + dog.name + " run")
dog = Dog("狗")
# dog = WhiteDog("白狗")
# dog = BlackDog("黑狗")
person = Person("小明")
person.run(dog)
3、类方法、实例方法与静态方法
与java不同,python的类方法和静态方法只指不同的方法。类方法只需要访问类属性,静态方法既不需要访问类属性也不需要访问实例属性,实例方法既可以访问类属性也可以访问实例属性。
pyhton的类方法需要添加@classmethod注释,同时方法中需要传入cls表示类的参数。静态方法需要@staticmethod注释
# 测试类方法与静态方法
class Dog:
# 类属性
name = "tom"
# 定义类方法(只需要访问类属性)
@classmethod
def eat(cls):
print(cls.name + " eat ")
# 定义静态方法(既不访问类属性也不访问实例属性)
@staticmethod
def help():
print("help message")
# 实例方法(既可以访问实例属性也可以访问类属性)
def run(self):
print("name:" + Dog.name + " color:" + self.color)
# 构造方法
def __init__(self, color):
self.color = color
dog = Dog("black")
dog.eat()
dog.help()
dog.run()
结果
tom eat
help message
name:tom color:black
4、单例模式
单例是指不管new出多少对象,这些对象都是同一个对象,即其地址是同一个。__new__(cls, *args, **kwargs)是为对象分配内存空间的方法,同时分配完内存空间后需要进行返回否则创建的对象为None。
# 单例设计模式
class Dog:
instance = None
flag = False
# 为对象分配空间
def __new__(cls, *args, **kwargs):
if Dog.instance is None:
Dog.instance = super().__new__(cls)
return Dog.instance
def __init__(self):
if Dog.flag:
return
print("开始初始化实例")
Dog.flag = True
dog1 = Dog()
dog2 = Dog()
print(dog1)
print(dog2)
结果
开始初始化实例
<__main__.Dog object at 0x0000019BA19C53A0>
<__main__.Dog object at 0x0000019BA19C53A0>
5、模块
每个python文件就相当于一个模块,使用时可以导入其他模块进行使用,如下所示,在testmodule2.py文件中,使用__name__使得在引用该模块时后面的模块中的测试语句不会被调用。
testmodule2.py 文件中
# 模块测试
def hello():
print("hello world")
class Dog:
def eat(self):
print("dog eat")
# 下面的代码其实是不需要的,同时别人在导入本模块时下面的代码会自动执行一遍
# 使用__name__可以解决,当本模块执行是__name__=__main__,当别人调用时__name__=该模块名称
# 这种方式通常用于模块测试
if __name__ == "__main__":
print("模块中没有缩进的代码")
tetmodule.py文件中
# 模块测试
import random
from testmodule2 import Dog as D
from testmodule2 import hello
# __file__可以查看模块的完整路径
print(random.__file__)
dog = D()
dog.eat()
hello()
运行testmodule文件
E:\python38\lib\random.py
dog eat
hello world
6、包
创建包,包下会自动生成__init__.py文件,该包下的模块如果想被其他包中的模块调用,可以在__init__.py文件中导入,如下所示
package1包中有testpackage1.py的文件,在默认包下有个testpackage.py的文件,想要导入package1包下的模块testpackage1,需要在__init__文件中
from . import testpackage1
testpackage1.py文件
class Dog:
def eat(self):
print("eat")
testpackage.py文件
from package1 import testpackage1
dog = testpackage1.Dog()
dog.eat()