python散装笔记——38: Classes - 类(3)

B站影视 2025-01-08 09:31 2

摘要:FooBar.mro[, , , ]

Python 使用 C3 线性化算法来确定类属性(包括方法)的解析顺序。这就是所谓的方法解析顺序 (MRO)。

下面是一个简单的例子:

class Foo(object):foo = 'attr foo of Foo'class Bar(object):foo = 'attr foo of Bar' # we won't see this.bar = 'attr bar of Bar'class foobar(Foo, Bar):foobar = 'attr foobar of FooBar'

现在,如果我们实例化 FooBar,如果我们查找 foo 属性,我们会发现 Foo 的属性被首先找到

fb = FooBar

>>> fb.foo'attr foo of Foo'

这是 FooBar 的 MRO:

FooBar.mro[, , , ]

可以简单地说,Python 的 MRO 算法是:

深度优先(例如,先是 FooBar,然后是 Foo),除非共享的父(对象)被子(Bar)阻塞,并且不允许循环关系。

例如,Bar 不能从 FooBar 继承,而 FooBar 从 Bar 继承。

有关 Python 中的综合示例,请参阅维基百科条目。

super 可以获取父类的特征。

class Foo(object):def foo_method(self):print "foo Method"class Bar(object):def bar_method(self):print "bar Method"class FooBar(Foo, Bar):def foo_method(self):super(FooBar, self).foo_method

当每个类都有自己的 __init__ 方法时,如果我们尝试多重继承,那么只有先继承的类的 __init__ 方法才会被调用。

下面的例子中,只有 Foo 类的初始方法被调用,Bar 类的初始方法没有被调用

class Foo(object):def __init__(self):print("foo init")class Bar(object):def __init__(self):print("bar init")class FooBar(Foo, Bar):def __init__(self):print("foobar init")super(FooBar, self).__init__a = FooBar

输出:

foobar initfoo init

但这并不意味着 Bar 类不能继承。最终 FooBar 类的实例也是 Bar 类和 Foo 类的实例。

print(isinstance(a,FooBar))print(isinstance(a,Foo))print(isinstance(a,Bar))

输出:

TrueTrueTrue8: 属性

Python 类支持属性,这些属性看起来就像普通的对象变量,但可以附加自定义行为和文档。

class MyClass(object):def __init__(self):self._my_string = ""@propertydef string(self):"""A profoundly important string."""return self._my_string@string.setterdef string(self, new_value):assert isinstance(new_value, str), \"Give me a string, not a %r!" % type(new_value)self._my_string = new_value@string.deleterdef x(self):self._my_string = None

类 MyClass 的对象看起来会有一个属性 .string,但它的行为现在受到严格控制:

mc = MyClassmc.string = "String!"print(mc.string)del mc.string

除了上述有用的语法外,属性语法还允许为这些属性添加验证或其他增强功能。这在公共应用程序接口(Public API)中尤其有用--在公共应用程序接口中,应为用户提供一定程度的帮助。

属性的另一个常见用途是使类能够显示 “虚拟属性”--实际上并不存储的属性,只有在请求时才会计算。

class Character(object):def __init__(self, name, max_hp):self._name = nameself._hp = max_hpself._max_hp = max_hp# Make hp read only by not providing a set method@propertydef hp(self):return self._hp# Make name read only by not providing a set method@propertydef name(self):return self.namedef take_damage(self, damage):self._hp -= damageself._hp = 0 if self.hp 0 else False@propertydef is_dead(self):return not self.is_alivebilbo = Character('Bilbo Baggins', 100)bilbo.hp# out : 100bilbo.hp = 200# out : AttributeError: can't set attribute# hp attribute is read only.bilbo.is_alive# out : Truebilbo.is_wounded# out : Falsebilbo.is_dead# out : Falsebilbo.take_damage( 50 )bilbo.hp# out : 50bilbo.is_alive# out : Truebilbo.is_wounded# out : Truebilbo.is_dead# out : Falsebilbo.take_damage( 50 )bilbo.hp# out : 0bilbo.is_alive# out : Falsebilbo.is_wounded# out : Falsebilbo.is_dead# out : True

如果变量包含不可变类型的值(如字符串),则可以像下面这样分配默认值

class Rectangle(object):def __init__(self, width, height, color='blue'):self.width = widthself.height = heightself.color = colordef area(self):return self.width * self.height# Create some instances of the classdefault_rectangle = Rectangle(2, 3)print(default_rectangle.color) # bluered_rectangle = Rectangle(2, 3, 'red')print(red_rectangle.color) # red

在构造函数中初始化可变对象(如列表)时需要小心谨慎。请看下面的示例:

class Rectangle2D(object):def __init__(self, width, height, pos=[0,0], color='blue'):self.width = widthself.height = heightself.pos = posself.color = colorr1 = Rectangle2D(5,3)r2 = Rectangle2D(7,8)r1.pos[0] = 4r1.pos # [4, 0]r2.pos # [4, 0] r2 的位置也发生了变化

造成这种行为的原因是,在 Python 中,缺省参数是在函数执行时绑定的,而不是在函数声明时绑定的。要获得一个不在实例间共享的缺省实例变量,应该使用这样的结构:

class Rectangle2D(object):def __init__(self, width, height, pos=None, color='blue'):self.width = widthself.height = heightself.pos = pos or [0, 0] # 默认值是 [0, 0]self.color = colorr1 = Rectangle2D(5,3)r2 = Rectangle2D(7,8)r1.pos[0] = 4r1.pos # [4, 0]r2.pos # [0, 0] r2 的位置没有改变

另请参阅可变默认参数Common Gotchas The Hitchhiker's Guide to Python和 “最小惊奇 ”以及可变默认参数「链接」

来源:山岚

相关推荐