当前位置 博文首页 > zy010101博客:python——类
面向对象技术是几乎所有的现代化的语言都从语法层面进行直接支持的,而类就是面向对象技术在现代编程语言中的实现。类可以抽象现实,将现实中的事物抽象为类。例如,人,每个人都是不同的,但是人身上的某些属性是共通的,每个人都有名字,年龄,性别,身高,体重等等信息。这些信息是所有人都具备的特征,我们就可以将通用的属性分离出来,作以抽象即可。
下面是一个类的例子,我们先来看看。然后详细解释一下其中的元素。
class Human:
"""抽象人的特征,形成类"""
def __init__(self, name, gender, age, height, weight):
self.name = name
self.gender = gender
self.age = age
self.height = height
self.weight = weight
def get(self):
return self.name, self.gender, self.age, self.height, self.weight
这个init函数需要详细说明,init函数两边是两个下划线,不是一个下划线,这点需要注意。另外,这个函数会在类实例化对象的时候被调用。(类比于C++,其实该函数就是类的构造函数)在Python中两边有双下划线的函数都是python默认的方法,用这种方式来避免和普通的方法发生冲突。
这个函数中有一个self参数,这个参数是必不可少的,并且必须放在第一个位置。这个参数将在Python调用这个方法创建实例时,自动传入。每个和实例相关联的方法在被调用的时候,都会自动传入self参数。self参数是指向实例本身的引用(类比于C++,self参数其实就是C++的this指针),能让实例访问类中的属性和方法。
例如下面的get()函数,它也有self参数。我们在调用的时候是不需要传入self参数的。下面是一个例子,创建了一个对象,并调用了get()方法。
class Human:
"""抽象人的特征,形成类"""
def __init__(self, name, gender, age, height, weight):
self.name = name
self.gender = gender
self.age = age
self.height = height
self.weight = weight
def get(self):
return self.name, self.gender, self.age, self.height, self.weight
man = Human("Zhao Si", "man", 18, 172, 67)
print(man.get())
程序执行结果如下所示:
通过上面的代码,我们知道创建一个对象使用man = Human("Zhao Si", "man", 18, 172, 67)
也知道了,如何使用对象的方法man.get()
这样就调用了Human类的get方法,使用类的属性也是相同的,即使用点,然后跟上属性的名字即可,例如:man.age
。
可以看到get函数返回了一个元组,正是我们通过__init__()传入的参数。在该函数中带有self前缀的变量就是这个类的属性(C++的数据成员,也称为类的属性)。
继承是面向对象的另外一大特征。抽象出事物的属性和行为之后,就有了一个类,例如上面的Human类抽象了人类的部分属性,人类又可以根据某些特征进行分类,例如根据肤色可以划分黄种人,白人,黑人等。这些细分之后的也可以被抽象,但是之前抽象出来的Human类已经有了很多的属性和行为,现在细分的就可以在之前抽象的Human类上做继承即可。
原来的类称为“基类”,继承基类的类称为“派生类”。派生类将会拥有基类的所有方法和属性。下面来看一个例子:
class Car: # 基类
def __init__(self, price, year, make):
self.price = price
self.year = year
self.make = make
def get(self):
return self.price, self.year, self.make
def set(self, price, year, make):
self.price = price
self.year = year
self.make = make
my_car = Car("20w", 2020, "Audi")
print(my_car.get())
class ElectricCar(Car): # 写在括号内的是基类
def __init__(self, price, year, make, endurance, time):
super().__init__(price, year, make) # 调用父类构造函数,使用super()方式。
self.endurance = endurance
self.time = time
def get(self): # 这里不调用父类的get函数是因为父类的get函数返回的是一个元组。
return self.time, self.endurance, self.price, self.year, self.make
def set(self, price, year, make, endurance, time):
super(ElectricCar, self).set(price, year, make) # 使用父类的set函数。
self.endurance = endurance
self.time = time
def func(self):
pass
执行程序,结果如下所示:
接下来对上面的代码进行部分说明,子类调用父类的构造函数使用super()方式,调用父类的set函数也是使用super()方式。子类还可以定义自己的属性和方法,例如:time属性,func方法。其中,子类的get方法和set方法都是重写父类的方法。
下面引用《python编程:从入门到实践》中的一段话,这段话值得被放在这里
模拟较复杂的物件(如电动汽车)时,需要解决一些有趣的问题。续航里程是电瓶的属性还
是汽车的属性呢?如果只描述一辆汽车,将方法 get_range()放在 Battery 类中也许是合适的,
但如果要描述一家汽车制造商的整个产品线,也许应该将方法 get_range()移到 ElectricCar 类
中。在这种情况下,get_range()依然根据电瓶容量来确定续航里程,但报告的是一款汽车的续
航里程。也可以这样做:仍将方法 get_range()留在 Battery 类中,但向它传递一个参数,如
car_model。在这种情况下,方法 get_range()将根据电瓶容量和汽车型号报告续航里程。
这让你进入了程序员的另一个境界:解决上述问题时,从较高的逻辑层面(而不是语法层面)
考虑;考虑的不是 Python,而是如何使用代码来表示实物。达到这种境界后,你会经常发现,对
现实世界的建模方法没有对错之分。有些方法的效率更高,但要找出效率最高的表示法,需要经
过一定的实践。只要代码像你希望的那样运行,就说明你做得很好!即便发现自己不得不多次尝
试使用不同的方法来重写类,也不必气馁。要编写出高效、准确的代码,都得经过这样的过程。