在上一个教程中,我们学习了如何定义一个基本的 Python 类并创建它的实例。在本教程中,我们将看到如何为我们的类创建实例方法和属性。实例方法需要在调用之前创建其类的一个对象。实例方法总是将self关键字作为第一个参数。它指向它所创建的类的实例,或对象。self参数使得实例方法可以访问同一对象的属性或其他方法。实例属性持有的值对于从一个类中创建的每个对象的实例来说都是唯一的。它们是只属于一个对象的变量,只能在该对象的范围内访问。让我们用一些代码看看实例方法和属性是如何工作的。
启动实例属性
class Monitor:
def __init__(self, model, resolution, screensize, price):
self.model = model
self.resolution = resolution
self.screensize = screensize
self.price = price
当实例被创建并准备被初始化时,__init__函数被调用。它是一个实例方法,用来设置对象中的东西。在上面的代码中,我们有一些对象的属性,也被称为实例属性。一个监视器可能有一个 model, resolution, screensize、 和 price.我们用实例属性来表示这些东西。
定义一个实例方法
class Monitor:
def __init__(self, model, resolution, screensize, price):
self.model = model
self.resolution = resolution
self.screensize = screensize
self.price = price
def getPrice(self):
return f"This monitor has a price of {self.price}"
上面突出显示的是一个向 Python 类添加第二个实例方法的例子。它与定义一个普通的函数很相似,因为同样使用了def关键字。像所有的实例方法一样,函数的第一个参数是 self.你可以使用一个以不同方式命名的变量来代替 self,但这是不可取的,因为 self 是 Python 中公认的惯例。
创建对象实例
class Monitor:
def __init__(self, model, resolution, screensize, price):
self.model = model
self.resolution = resolution
self.screensize = screensize
self.price = price
def getPrice(self):
return f"This monitor has a price of {self.price}"
monitor1 = Monitor("Samsung", "1920 x 1080", "24 inch", 129.99)
monitor2 = Monitor("Viewsonic", "1920 x 1080", "24 inch", 109.99)
monitor3 = Monitor("Dell", "1920 x 1080", "27 inch", 159.99)
现在我们可以从这个类中创建一些对象实例了。在这里,我们创建了三个不同的对象,每个对象代表一个监视器。在创建每个对象的过程中,我们传入所需的参数,以便每个对象都能用正确的值来初始化其实例属性。
调用一个实例方法
class Monitor:
def __init__(self, model, resolution, screensize, price):
self.model = model
self.resolution = resolution
self.screensize = screensize
self.price = price
def getPrice(self):
return f"This monitor has a price of {self.price}"
monitor1 = Monitor("Samsung", "1920 x 1080", "24 inch", 129.99)
monitor2 = Monitor("Viewsonic", "1920 x 1080", "24 inch", 109.99)
monitor3 = Monitor("Dell", "1920 x 1080", "27 inch", 159.99)
print(monitor2.getPrice())
This monitor has a price of 109.99
每个对象都可以访问我们定义的实例方法,也可以访问所有的实例属性。这里我们在第二个被创建的对象上调用.getPrice()实例方法。 monitor2.它正确地伸入对象中找到它的价格,并让我们知道这个显示器的价格是109.99。
初始化之外的实例属性
class Monitor:
def __init__(self, model, resolution, screensize, price):
self.model = model
self.resolution = resolution
self.screensize = screensize
self.price = price
def getPrice(self):
return f"This monitor has a price of {self.price}"
def setDiscount(self, amount):
self._discount = amount
实例属性并不只针对 __init__ 函数,它们也可以在对象的其他地方被定义。这正是我们在上面做的,当我们在监视器上设置一个折扣时。我们添加了一个名为 setDiscount() 的新实例方法。该方法的第一个参数是self,但也有第二个金额参数。当setDiscount()方法被调用时,传入的金额值被分配到_discount的实例属性中。前面的下划线是为了暗示这个属性是类的内部属性,不应该从类的逻辑之外被访问。在其它[面向对象的]语言中,这就是你要使用private关键字的地方,但是 Python 没有这个功能。
Python hasattr()
当实例属性在 __init__ 方法中被定义时,程序员可以指望当一个对象被创建并投入使用时,这些属性值是可用的。对于在 __init__ 方法之外的实例属性,情况并非如此。在我们的例子中,如果setDiscount()方法在对象被创建后被调用,Monitor对象才会有一个_discount实例属性。否则,这个实例属性是不存在的。为了说明这种情况,Python 有一个[内置]的 hasattr() 函数。
class Monitor:
def __init__(self, model, resolution, screensize, price):
self.model = model
self.resolution = resolution
self.screensize = screensize
self.price = price
def getPrice(self):
if hasattr(self, "_discount"):
return f"This monitor has a price of {self.price - self._discount:.2f}"
else:
return f"This monitor has a price of {self.price}"
def setDiscount(self, amount):
self._discount = amount
monitor1 = Monitor("Samsung", "1920 x 1080", "24 inch", 129.99)
print(monitor1.getPrice())
# This monitor has a price of 129.99
monitor1.setDiscount(10)
print(monitor1.getPrice())
# This monitor has a price of 119.99
我们的监视器对象可能有也可能没有 _discount 实例属性。在上面的代码中,我们使用 hasattr() 函数来确定 _discount 实例属性是否被预设,如果是,我们改变 getPrice() 方法中的行为。这是一个很好的例子,说明为什么你可能需要使用 hasattr() 函数。
双下划线实例属性
当我们为 _discount 属性使用单引号下划线时,它给了我们一个提示,这个属性是要被隐藏的。实际上,Python 根本没有强制执行这一点。还有一个更严格的选择,那就是为实例属性使用双前导下划线。如果你使用双下划线作为一个属性或方法名称的开头,那么Python解释器将改变该属性或方法的名称,这样其他类在试图访问它时就会得到一个错误。
class Monitor:
def __init__(self, model, resolution, screensize, price):
self.model = model
self.resolution = resolution
self.screensize = screensize
self.price = price
self.__revision = "A"
def getPrice(self):
if hasattr(self, "_discount"):
return f"This monitor has a price of {self.price - self._discount:.2f}"
else:
return f"This monitor has a price of {self.price}"
def setDiscount(self, amount):
self._discount = amount
monitor1 = Monitor("Samsung", "1920 x 1080", "24 inch", 129.99)
print(monitor1.__revision)
Traceback (most recent call last):
File "C:/python/justhacking/lists.py", line 20, in
print(monitor1.__revision)
AttributeError: 'Monitor' object has no attribute '__revision'
如我们所见,运行上面的代码会产生一个属性错误。发生这种情况的原因是,由于我们使用了双下划线,Python动态地改变了属性的名称,在一个被称为名称混淆的过程中,将属性的名称与类的名称前缀起来。这是为了防止子类无意中覆盖该属性。在现实中,如果你简单地像这样调用代码。
print(monitor1._Monitor__revision)
A
那么代码就会运行。这不是一个完美的解决方案,但它确实在需要时提供了某种程度的数据隐藏。如果需要的话,你可以使用这种方法来确保子类不会使用与你已经使用过的属性相同的名称。在某些情况下,你会希望能够重写该属性。