Skip to main content

面向对象编程

面向对象编程

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

在Python中,所有数据类型都可以视为对象,当然也可以自定义对象。自定义的对象数据类型就是面向对象中的类(Class)的概念。

面向对象的设计思想是从自然界中来的,因为在自然界中,类(Class)和实例(Instance)的概念是很自然的。Class是一种抽象概念,比如我们定义的Class——Student,是指学生这个概念,而实例(Instance)则是一个个具体的Student,比如,张三和李四是两个具体的Student。

所以,面向对象的设计思想是抽象出Class,根据Class创建Instance。

面向对象的抽象程度又比函数要高,因为一个Class既包含数据,又包含操作数据的方法。

创建类

类的特殊方法

Python 使用 __ 开头的名字来定义特殊的方法和属性,它们有:

__init__()
__repr__()
__str__()
__call__()
__iter__()
__add__()
__sub__()
__mul__()
__rmul__()
__class__
__name__

构造方法 __init__()

在产生对象之后,我们可以向对象中添加属性。 事实上,还可以通过构造方法,在构造对象的时候直接添加属性:

class Clothes(object):
"""
init_demo
"""

def __init__(self, color="green"):
self.color = color


my_clothes = Clothes()
my_clothes.color

传入有参数的值:

your_clothes = Clothes('orange')
your_clothes.color

表示方法 __repr__() 和 __str__():

class Clothes(object):
"""
repr and str demo
"""

def __init__(self, color="green"):
self.color = color

def __str__(self):
"This is a string to print."
return ("a {} clothes".format(self.color))

def __repr__(self):
"This string recreates the object."
return ("{}(color='{}')".format(self.__class__.__name__, self.color))

__str__() 是使用 print 函数显示的结果,类似java中的toString:

my_clothes = Clothes()
print(my_clothes)

__repr__() 返回的是不使用 print 方法的结果:

my_clothes
print(my_clothes.__class__, my_clothes.__class__.__name__, my_clothes.color)
my_clothes.__class__, my_clothes.__class__.__name__, my_clothes.color

类的属性

只读属性:

class Clothes(object):
def __init__(self, price):
self.price = price

# 这样 discount_price 就变成属性了
@property
def discount_price(self):
return self.price * 0.8

这里 discount_price 就是一个只读不写的属性了(注意是属性不是方法), 而price是可读写的属性:

my_clothes = Clothes(100)
print(my_clothes.discount_price) # 80.0

可以修改price属性来改变discount_price:

my_clothes.price = 200
print(my_clothes.discount_price) # 160.0

my_clothes.discount_price()会报错,因为 my_clothes.discount_price 是属性,不是方法;

my_clothes.discount_price=100 也会报错,因为只读。

对于 @property 生成的只读属性,我们可以使用相应的 @attr.setter 修饰符来使得这个属性变成可写的:

class Clothes(object):
def __init__(self, price):
self.price = price

# 这样就变成属性了
@property
def discount_price(self):
return self.price * 0.8

@discount_price.setter
def discount_price(self, new_price):
self.price = new_price * 1.25

测试一下:

my_clothes = Clothes(100)
print(my_clothes.discount_price)

my_clothes.price = 200
print(my_clothes.discount_price)

修改 discount_price 属性:

my_clothes.discount_price = 180
print(my_clothes.price)
print(my_clothes.discount_price)

一个等价的替代如下,用方法:

class Clothes(object):
def __init__(self, price):
self.price = price

def get_discount_price(self):
return self.price * 0.8

def set_discount_price(self, new_price):
self.price = new_price * 1.25

discount_price = property(get_discount_price, set_discount_price)

my_clothes = Clothes(100)
print(my_clothes.discount_price)

my_clothes.price = 200
print(my_clothes.discount_price)

my_clothes.discount_price = 180
print(my_clothes.price)
print(my_clothes.discount_price)

继承

类定义的基本形式:

class ClassName(ParentClass):
"""class docstring"""
def method(self):
return

里面的 ParentClass 就是用来继承的。

class Clothes(object):
def __init__(self, color="green"):
self.color = color

def out_print(self):
return self.__class__.__name__, self.color
my_clothes = Clothes()
my_clothes.color
my_clothes.out_print()

定义一个子类,继承父类的所有方法:

class NikeClothes(Clothes):
def change_color(self):
if self.color == "green":
self.color = "red"

继承父类的所有方法:

your_clothes = NikeClothes()
your_clothes.color
your_clothes.out_print()

但有自己的方法:

your_clothes.change_color()
your_clothes.color

如果想对父类的方法进行修改,只需要在子类中重定义这个类即可:

class AdidasClothes(Clothes):
def change_color(self):
if self.color == "green":
self.color = "black"

def out_print(self):
self.change_color()
return self.__class__.__name__, self.color


him_clothes = AdidasClothes()
print(him_clothes.color)

him_clothes.change_color()
print(him_clothes.color)
print(him_clothes.out_print())

super() 函数

super(CurrentClassName, instance)

返回该类实例对应的父类对象。

刚才 AdidasClothes可以改写为:

class NewAdidasClothes(Clothes):
def change_color(self):
if self.color == "green":
self.color = "black"

def out_print(self):
self.change_color()
print(super(NewAdidasClothes, self).out_print())

her_clothes = NewAdidasClothes()
print(her_clothes.color)

her_clothes.out_print()

new() 方法

new()用来创建一个实例,它至少有一个参数cls,代表当前类。默认情况下__new__()会创建当前类的实例,该方法也可以被重载,重载后也可以创建其他类的实例。

class Fun(object):
def __init__(self, fun):
self.fun = fun

def __new__(cls, *args, **kwargs):
return object.__new__(Fun)

if __name__ == '__main__':
f = Fun.__new__(Fun)
print(type(f))

new()方法只是创建实例,此时拿到的实例并不能正常使用。一个实例需要被__init__()方法初始化后才可以被正常使用。也就是说,正常场景下,我们生成一个类的实例,Python先调用该类的__new()方法创建一个实例,然后再调用__init()方法初始化该实例。__new()__方法存在于object方法中,通常情况下不需要被重载。

可以使用__new__方法创建出其它类的实例。在这种场景下,__new__方法创建后会调用对应类的__init__方法完成初始化:

class Fun(object):
def __init__(self, fun):
self.fun = fun

def __new__(cls, *args, **kwargs):
return Demo(*args, **kwargs)


class Demo(object):
def __init__(self, d):
self.demo = d


if __name__ == '__main__':
f = Fun(1)
print("type f:", type(f))
print("f.demo:", f.demo)

可以看出,f不是Fun类的一个实例,而是Demo类的一个实例,拥有Demo类的字段。因为Fun类的__new__方法创建的是一个Demo类实例,而非Fun类本身。因此Fun.__new__方法在return后调用了Demo.__init__方法,以完成该实例的初始化。

接口

接口的调用:

class Clothes(object):
def __init__(self, color="green"):
self.color = color

def out(self):
print("father.")


class NikeClothes(Clothes):
def out(self):
self.color = "brown"
super(NikeClothes, self).out()


class AdidasClothes(object):
def out(self):
print("adidas.")

因为三个类都实现了 out() 方法,因此可以这样使用:

objects = [Clothes(), NikeClothes(), AdidasClothes()]
for obj in objects:
obj.out()

类方法

类方法包括以下几种:

  1. special 方法和属性,即以 __ 开头和结尾的方法和属性
  2. 私有方法和属性,以 _ 开头,不过不是真正私有,而是可以调用的, 但是不会被代码自动完成所记录(即 Tab 键之后不会显示)
  3. 共有的方法和属性

__ 开头不以 __ 结尾的属性是更加特殊的方法,调用方式也不同:

class MyDemoClass(object):
def __init__(self):
print("special.")

def _get_name(self):
print("_get_name is private method.")

def get_value(self):
print("get_value is public method.")

def __get_type(self):
print("__get_type is really special method.")
demo = MyDemoClass()

demo.get_value()
demo._get_name()
demo._MyDemoClass__get_type()