30天学习Python👨💻第十天——面向对象补充
昨天探讨了面向对象所有重要的概念。今天我会探讨面向对象剩下的概念以及它们在Python中的实现。除此之外,我尝试用一些涵盖面向对象所有概念用法的编程练习来稳固之前所有的概念。
Super()
super是Python中的一个保留字(在Python2.2中被引入),在继承中使用。当一个子类继承自一个父类,并且需要调用这个父类上的方法时,就需要用到super。这样听起来可能让人有些疑惑,来看一个例子:
不使用super
class Employee:
def __init__(self, name):
self.name = name
print(f'{self.name} is an employee')
class Manager(Employee):
def __init__(self, department, name):
self.department = department
self.name = name
Employee.__init__(self, name) # 调用父类的构造函数
print(f'Manager, {self.department} department')
staff_1 = Manager('HR', 'Andy')
# Andy is an employee
# Manager, HR department
在这个例子中,父类的__init__构造方法用类名调用,self对象作为第一个参数被传入。
使用super(注意它不需要传入参数self)
class Employee:
def __init__(self, name):
self.name = name
print(f'{self.name} is an employee')
class Manager(Employee):
def __init__(self, department, name):
self.department = department
self.name = name
super().__init__(name) # 调用父类的构造函数
print(f'Manager, {self.department} department')
staff_1 = Manager('HR', 'Andy')
# Andy is an employee
# Manager, HR department
像上面代码中的构造方法一样,父类的任何方法在子类中都能够使用super()调用。
在JavaScript中,语法更简单一点,super的调用方式类似super(parameter)这样。但是我喜欢Python的语法。使用super调用__init__方法更加清楚。
内省
Python能够计算在运行时计算对象的类型(在Python中一切皆对象)。这意味着编译器在运行时能够动态的理解对象的属性和方法。这叫做内省。
Python提供了一个内置函数dir来对一个对象进行内省。
class Developer:
def __init__(self, name, language):
self.name = name
self.language = language
def introduce(self):
print(f'Hi! I am {self.name}. I code in {self.language}')
dev = Developer('Matt', 'Python')
print(dir(dev))
魔术方法
在Python中,通过定义一些魔术方法类会变得更加强大。这些方法名的前面和后面都有两个__,在Python中被预先定义,有着特殊的用途。例如,我们可以访问内置函数,因为它被定义为一个特殊的魔术方法__len__
class Sentence:
words = []
def add_word(self, word):
self.words.append(word)
def __len__(self):
return len(self.words)
new_sentence = Sentence()
new_sentence.add_word('Hello')
new_sentence.add_word('World')
print(len(new_sentence))
我修改了Sentence类,这样我们就可以使用内置方法len,默认情况下,len不能用于实现自定义逻辑。魔术方法看上去非常的方便。
多继承
一个类从多个类中继承属性和方法是有可能的。多继承是一个强大的概念,但也是危险的。在JavaScript中,多继承是不被支持的。
class Batsman:
def swing_bat(self):
return 'What a shot!'
class Bowler:
def bowl_bouncer(self):
return 'What a bouncer!'
class AllRounder(Batsman, Bowler):
pass
player = AllRounder()
print(player.bowl_bouncer()) # What a shot!
print(player.swing_bat()) # What a bouncer!
当父类有需要初始化的构造函数方法时,它可能会变得有点复杂。在子类中,它所继承所有类的构造函数方法都需要初始化。
class Batsman:
def __init__(self, hitting_power):
self.hitting_power = hitting_power
def swing_bat(self):
return f'Shot with power {self.hitting_power}'
class Bowler:
def __init__(self, delivery_speed):
self.delivery_speed = delivery_speed
def bowl_bouncer(self):
return f'Bowled with speed of {self.delivery_speed} kmph'
class AllRounder(Batsman, Bowler):
def __init__(self, hitting_power, delivery_speed):
Batsman.__init__(self, hitting_power)
Bowler.__init__(self, delivery_speed)
player = AllRounder(90, 80)
print(player.swing_bat())
print(player.bowl_bouncer())
方法解析顺序
方法解析顺序或简称mro,是Python中属性和方法继承的顺序。
当从多个类继承时,属性和方法由特定层次结构中的子类继承。Python中实现此功能的底层算法使用深度优先搜索算法。
class Employee:
secret_code = 'secret'
class Manager(Employee):
secret_code = 'm123'
class Accountant(Employee):
secret_code = 'a123'
class Owner(Manager, Accountant):
pass
person = Owner()
print(person.secret_code) # m123
为了了解继承顺序,Python提供了一个方法mro,可以在对象上调用该方法来查看继承的层次结构。
多继承可能很难理解,所以这种模式在实践中并不常用。
这就是今天所有的内容了。最后讲完了Python中的面向对象编程概念。我们的目标是在这个挑战结束后,开始构建真正的Python项目时使用这些原则。
我希望我能够涵盖Python中所有关键的面向对象编程概念,并且解释的足够通俗易懂。
明天我们将进入Python函数式编程的领域。肯定会很有趣。总的来说,Python是一种程序语言,并且因其面向对象的概念而受到欢迎,在本周余下的时间里,我们将探索如何用Python实现函数式编程概念。