Skip to main content

类与对象

info

数据类自动生成常用的方法如__init____repr__等,减少样板代码,让开发者专注于业务逻辑。

from dataclasses import dataclass

@dataclass
class Person:
name: str
age: int

PEP 557 – 数据类

面向对象编程

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

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

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

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

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

class

python通过class关键字定义一个类,并创建一个实例。

class ClassName:
"""class docstring"""
pass

instance = ClassName()

类的特殊方法

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()

type函数

object

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()

自定义异常类

异常是标准库中的类,这意味着我们可以自定义异常类:

尝试在文本输入框输入:k,start,q

class CommandError(ValueError):
print("bad command operation. must input 'start', 'stop', 'pause'")

valid_commands = {'start', 'stop', 'pause'}
while True:
command = input('>')
if command == 'q':
break
try:
if command.lower() not in valid_commands:
raise CommandError('Invalid command: %s' % command)
print('input command:', command)
except CommandError:
print("bad command string: %s" % command)

with

内置函数

type函数、isinstance函数、issubclass函数

dir函数、hasattr函数、delattr函数、setattr函数、getattr函数、vars函数

表格说明:

函数说明
dir获取对象的所有属性和方法
hasattr检查对象是否具有某个属性
delattr删除对象的属性
setattr设置对象的属性
getattr获取对象的属性
vars获取对象的属性字典

元类编程