步骤1:创建实例
# File person.py(start)
class Person: # start a class
编写构造函数
构造函数包含了每次创建一个实例时python都会自动运行的代码:
class Person:
def __init__(self, name, job, pay):
self.name = name
self.job = job
self.pay = pay
我们传入的数据作为构造函数方法的参数附加到一个实例上,并且将它们赋给self以长期持有。在OOP术语中,self就是创建的实例对象。
注意,这里每个参数都可以出现了两次。这段代码乍一看有点多余,但实际上不是这样。例如job参数在__init__函数的作用域里是一个局部变量,二self.job则是实例的一个属性,它隐含了方法调用的主体。这是两个不同变量,但恰好具有相同的名称。
构造函数可以传递默认值参数:
class Person:
def __init__(self, name, job=None, pay=0):
self.name = name
self.job = job
self.pay = pay
在行进中测试
class Person:
def __init__(self, name, job=None, pay=0):
self.name = name
self.job = job
self.pay = pay
bob = Person('Bob Smith')
sue = Person('Sue Jones', job='dev', pay=0)
print(bob.name, bob.job)
print(sue.name, sue.job)
从技术上讲,bob和sue和所有的其他实例一样都是命名空间对象,它们都拥有属于自己的类所创建的状态信息的独立副本;
以两种方式使用代码
当以上代码作为模块导入时,底部的代码将被自动执行,但是这种打印往往是一些测试代码,在模块引用时并不关心代码中的测试,下面代码格式可以避免这一问题:
class Person:
def __init__(self, name, job=None, pay=0):
self.name = name
self.job = job
self.pay = pay
if __name__ == '__main__'
bob = Person('Bob Smith')
sue = Person('Sue Jones', job='dev', pay=0)
print(bob.name, bob.job)
print(sue.name, sue.job)
步骤2:添加行为方法
编写方法
对于数据的操作的方法,我们不希望每次都重复编写,而是希望编写一次在需要使用是直接调用。需要完成这一操作,我们需要将操作对象的代码编写到类方法中,而不是分散在整个程序中;
class Person:
def __init__(self, name, job=None, pay=0):
self.name = name
self.job = job
self.pay = pay
def lastname(self):
# 编写获取名字的方法
return self.name.split()[-1]
def giveraise(self, persent):
# 编写涨工资的代码
self.pay = int(self.pay * (1 + persent))
if __name__ == '__main__'
bob = Person('Bob Smith')
sue = Person('Sue Jones', job='dev', pay=10000)
print(bob.name, bob.job) # output:('Bob Smith', None)
print(sue.name, sue.job) # output:('Sue Jones', 'dev')
print(bob.lastname(), sue.lastname()) # output:('Smith', 'Jones')
sue.giveraise(0.1)
print(sue.pay) # output:11000
正如上面所显示的,方法知识附加给类并旨在处理那些类的实例的常规函数,实例时方法调用的主体,并且会自动传给方法的self参数;
步骤3:运算符重载
提供打印显示
我们已经学过__init __这一运算重载的方法,接下来我们学习第二个常用的运算符重载的方法__repr __;
class Person:
def __init__(self, name, job=None, pay=0):
self.name = name
self.job = job
self.pay = pay
def lastname(self):
# 编写获取名字的方法
return self.name.split()[-1]
def giveraise(self, persent):
# 编写涨工资的代码
self.pay = int(self.pay * (1 + persent))
def __repr__(self):
# 使用重载运算符
return '[Person: %s, %s]' % (self.name, self.pay)
if __name__ == '__main__':
bob = Person('Bob Smith')
sue = Person('Sue Jones', job='dev', pay=10000)
print(bob) # output:[Person:Bob Smith, 0]
print(sue) # output:[Person:Sue Jones, 10000]
print(bob.lastname(), sue.lastname()) # output:('Smith', 'Jones')
sue.giveraise(0.1)
print(sue) # output:[Person:Sue Jones, 11000]
步骤4:通过编写字类定制行为
编写子类
我们需要定义一个名为Manager的新类,它继承自父类Person:
class Manager(Person):
假设Manager的涨薪方式不同,我们需要重新编写,有两种方式进行重新定义;
扩展方法:不好的方式
class Manager(Person):
def giveraise(self, percent, bonus=0.1):
self.pay = int(self.pay * (1 + persent + bonus))
以上这个方式的问题在于:每当你复制粘贴代码时,基本上都会使未来的维护工作倍增。考虑一下:由于我们复制了最初的版本,如果一旦改变了涨工资的方式(这完全是可能的),就必须修改两个地方而不是一个地方的代码;
扩展方法:好的方式
class Manager(Person):
def giveraise(self, percent, bonus=0.1):
Person.giveraise(self, percent + bonus)
这段代码利用了这样一个事实:类方法总是可以在一个实例中调用,或者通过类来调用。用更加符号化的形式来说,记住如下的常规方法调用:
instance.method(args....)
有python自动地转换为如下的同等形式:
class.method(instance, args.....)
多态的应用
class Person:
def __init__(self, name, job=None, pay=0):
self.name = name
self.job = job
self.pay = pay
def lastname(self):
# 编写获取名字的方法
return self.name.split()[-1]
def giveraise(self, persent):
# 编写涨工资的代码
self.pay = int(self.pay * (1 + persent))
def __repr__(self):
# 使用重载运算符
return '[Person: %s, %s]' % (self.name, self.pay)
class Manager(Person):
def giveraise(self, percent, bonus=0.1):
Person.giveraise(self, percent + bonus)
if __name__ == '__main__':
bob = Person('Bob Smith')
sue = Person('Sue Jones', job='dev', pay=10000)
print(bob) # output:[Person:Bob Smith, 0]
print(sue) # output:[Person:Sue Jones, 10000]
print(bob.lastname(), sue.lastname()) # output:('Smith', 'Jones')
sue.giveraise(0.1)
print(sue) # output:[Person:Sue Jones, 11000]
tom = Manager('Tom Jones', 'mgr', 50000)
tom.giveraise(0.1)
print(tom.lastname()) # output:Jones
print(tom) # output:[Person:Tom Jones, 60000]
print(...All three...)
for obj in (bob, sue, tom):
obj.giveraise(0.1)
print(obj) # output:[Person:Bob Smith, 0]
# [Person:Sue Jones, 11000]
# [Person:Tom Jones, 60000]
上述的for循环内的代码体现的就是多态:不同的实例可以通过相同的方式调用同一种方法;
继承、定制和扩展
通常,类可以继承、定制或扩展父类中已有代码;
class Person:
def lastName(self):...
def giveRaise(self):...
def __repr__(self):...
class Manager(Person):
def giveRaise(self, ...):....
def someThingElse(self, ...):...
tom = Manager()
tom.lastName()
tom.someThingElse()
print(tom)
OOP:宏观理念
在OOP中,我们通过已经介绍过的定制来编程,而且不是复制和修改已有代码。使用类的所揭示的编程风格和其他的方法相比会显著地减少开发时间;
步骤5:定制构造函数
有时候根据需要,需要重新的编写构造函数。
class Person:
def __init__(self, name, job=None, pay=0):
self.name = name
self.job = job
self.pay = pay
def lastname(self):
# 编写获取名字的方法
return self.name.split()[-1]
def giveraise(self, persent):
# 编写涨工资的代码
self.pay = int(self.pay * (1 + persent))
def __repr__(self):
# 使用重载运算符
return '[Person: %s, %s]' % (self.name, self.pay)
class Manager(Person):
def __init__(self, name, pay)
Person.__init__(self, name, 'mgr', pay) # 调用父类中的构造函数
def giveraise(self, percent, bonus=0.1):
Person.giveraise(self, percent + bonus)
if __name__ == '__main__':
bob = Person('Bob Smith')
sue = Person('Sue Jones', job='dev', pay=10000)
print(bob) # output:[Person:Bob Smith, 0]
print(sue) # output:[Person:Sue Jones, 10000]
print(bob.lastname(), sue.lastname()) # output:('Smith', 'Jones')
sue.giveraise(0.1)
print(sue) # output:[Person:Sue Jones, 11000]
tom = Manager('Tom Jones', 50000)
tom.giveraise(0.1)
print(tom.lastname()) # output:Jones
print(tom) # output:[Person:Tom Jones, 60000]
OOP比你认为的要简单
上面的代码几乎涵盖了python中OOP机制中几乎所有重要概念:
- 实例创建——填充实例属性;
- 行为方法——在类的方法中封装逻辑;
- 运算符重载——为像打印这样的内置操作提供行为;
- 定制行为——重新定义子类中的方法以使其特殊化;
- 定制构造函数——向父类的初始化步骤中添加逻辑;
步骤6:使用内省工具
特殊的类属性
- 内置的instance.__class __属性提供了一个从实例到创建它的类的链接。同时类有一个__name __,还有一个__bases __序列来提供父类的访问;
>>> class FirstClass: pass
...
>>> class SecondClass(FirstClass): pass
...
>>> class NewClass(SecondClass): pass
...
>>> N = NewClass()
>>> N.__class__
<class '__main__.NewClass'>
>>> NewClass.__name__
'NewClass'
>>> NewClass.__bases__
(<class '__main__.SecondClass'>,)
- 内置的object.__dict __属性提供了一个字典,将所有命名空间对象中的属性都储存为键/值对;
一种通用显示工具
class AttrDisplay:
'''
说明文本:通用显示工具
'''