面向对象的 Python - 构建块
在本章中,我们将详细讨论面向对象的术语和编程概念。类只是一个实例的工厂。该工厂包含描述如何制作实例的蓝图。实例或对象是从类构造的。在大多数情况下,我们可以拥有一个类的多个实例。每个实例都有一组属性,并且这些属性在类中定义,因此特定类的每个实例都应该具有相同的属性。
类包:Behave和状态
类可以让您将对象的Behave和状态捆绑在一起。观察下图以便更好地理解 -
在讨论类包时,以下几点值得注意 -
Behave这个词与功能相同——它是一段执行某事(或实现某种Behave)的代码
状态这个词与变量相同——它是在类中存储值的地方。
当我们一起断言一个类的Behave和状态时,这意味着一个类封装了函数和变量。
类有方法和属性
在 Python 中,创建方法定义类Behave。“方法”这个词是为类中定义的函数指定的 OOP 名称。总结一下 -
类函数- 是方法的同义词
类变量- 是名称属性的同义词。
类- 具有精确Behave的实例的蓝图。
对象- 类的实例之一,执行类中定义的功能。
类型- 表示实例所属的类
属性- 任何对象值:object.attribute
方法- 类中定义的“可调用属性”
例如,观察以下代码片段 -
var = “Hello, John” print( type (var)) # ‘str’> or <class 'str'> print(var.upper()) # upper() method is called, HELLO, JOHN
创建和实例化
下面的代码展示了如何创建我们的第一个类及其实例。
class MyClass(object): pass # Create first instance of MyClass this_obj = MyClass() print(this_obj) # Another instance of MyClass that_obj = MyClass() print (that_obj)
这里我们创建了一个名为MyClass 的类,它不执行任何任务。MyClass类中的参数对象涉及类继承,将在后面的章节中讨论。传入上面的代码表明该块是空的,即它是一个空的类定义。
让我们创建MyClass()类的实例this_obj并打印它,如下所示 -
<__main__.MyClass object at 0x03B08E10> <__main__.MyClass object at 0x0369D390>
在这里,我们创建了MyClass 的一个实例。十六进制代码指的是存储对象的地址。另一个实例指向另一个地址。
现在让我们在类MyClass()中定义一个变量,并从该类的实例中获取该变量,如以下代码所示 -
class MyClass(object): var = 9 # Create first instance of MyClass this_obj = MyClass() print(this_obj.var) # Another instance of MyClass that_obj = MyClass() print (that_obj.var)
输出
执行上面给出的代码时,您可以观察到以下输出 -
9 9
由于实例知道它是从哪个类实例化的,因此当从实例请求属性时,实例会查找该属性和类。这称为属性查找。
实例方法
类中定义的函数称为方法。实例方法需要一个实例才能调用它,并且不需要装饰器。创建实例方法时,第一个参数始终是self。尽管我们可以用任何其他名称来称呼它(self),但建议使用 self,因为它是一种命名约定。
class MyClass(object): var = 9 def firstM(self): print("hello, World") obj = MyClass() print(obj.var) obj.firstM()
输出
执行上面给出的代码时,您可以观察到以下输出 -
9 hello, World
请注意,在上面的程序中,我们定义了一个以 self 作为参数的方法。但我们无法调用该方法,因为我们尚未声明任何参数。
class MyClass(object): def firstM(self): print("hello, World") print(self) obj = MyClass() obj.firstM() print(obj)
输出
执行上面给出的代码时,您可以观察到以下输出 -
hello, World <__main__.MyClass object at 0x036A8E10> <__main__.MyClass object at 0x036A8E10>
封装
封装是 OOP 的基础之一。OOP 使我们能够隐藏对象内部工作的复杂性,这对开发人员有以下好处 -
简化并易于理解在不了解内部结构的情况下使用对象。
任何变化都可以轻松管理。
面向对象编程严重依赖封装。术语封装和抽象(也称为数据隐藏)通常用作同义词。它们几乎是同义词,因为抽象是通过封装实现的。
封装为我们提供了限制对对象某些组件的访问的机制,这意味着从对象定义的外部无法看到对象的内部表示。访问这些数据通常是通过特殊方法来实现的——Getters和Setters。
该数据存储在实例属性中,并且可以从类外部的任何位置进行操作。为了确保其安全,只能使用实例方法来访问该数据。不应允许直接访问。
class MyClass(object): def setAge(self, num): self.age = num def getAge(self): return self.age zack = MyClass() zack.setAge(45) print(zack.getAge()) zack.setAge("Fourty Five") print(zack.getAge())
输出
执行上面给出的代码时,您可以观察到以下输出 -
45 Fourty Five
仅当数据正确且有效时,才应使用异常处理结构来存储数据。正如我们在上面看到的,setAge() 方法对用户输入没有限制。它可以是字符串、数字或列表。所以我们需要检查上面的代码以确保存储的正确性。
class MyClass(object): def setAge(self, num): self.age = num def getAge(self): return self.age
zack = MyClass() zack.setAge(45) print(zack.getAge()) zack.setAge("Fourty Five") print(zack.getAge())
初始化构造函数
一旦实例化类的对象,就会隐式调用__ init __ 方法。这将初始化该对象。
x = MyClass()
上面显示的代码行将创建一个新实例并将该对象分配给局部变量 x。
实例化操作,即调用类对象,创建一个空对象。许多类喜欢创建具有定制为特定初始状态的实例的对象。因此,一个类可以定义一个名为“__init__()”的特殊方法,如下所示 -
def __init__(self): self.data = []
Python 在实例化期间调用 __init__ 来定义一个附加属性,该属性在实例化类时应出现,该属性可能会为该对象设置一些起始值或运行实例化所需的例程。因此,在这个例子中,可以通过以下方式获得一个新的初始化实例 -
x = MyClass()
__init__() 方法可以具有单个或多个参数,以获得更大的灵活性。init 代表初始化,因为它初始化实例的属性。它被称为类的构造函数。
class myclass(object): def __init__(self,aaa, bbb): self.a = aaa self.b = bbb x = myclass(4.5, 3) print(x.a, x.b)
输出
4.5 3
类属性
在类中定义的属性称为“类属性”,在函数中定义的属性称为“实例属性”。定义时,这些属性不以 self 为前缀,因为它们是类的属性,而不是特定实例的属性。
类属性可以由类本身 (className.attributeName) 以及类的实例 (inst.attributeName) 访问。因此,实例可以访问实例属性和类属性。
>>> class myclass(): age = 21 >>> myclass.age 21 >>> x = myclass() >>> x.age 21 >>>
类属性可以在实例中被重写,尽管这不是破坏封装的好方法。
Python 中有一个属性的查找路径。第一个是类中定义的方法,然后是它上面的类。
>>> class myclass(object): classy = 'class value' >>> dd = myclass() >>> print (dd.classy) # This should return the string 'class value' class value >>> >>> dd.classy = "Instance Value" >>> print(dd.classy) # Return the string "Instance Value" Instance Value >>> >>> # This will delete the value set for 'dd.classy' in the instance. >>> del dd.classy >>> >>> # Since the overriding attribute was deleted, this will print 'class value'. >>> print(dd.classy) class value >>>
我们正在覆盖实例 dd 中的“classy”类属性。当它被覆盖时,Python 解释器会读取被覆盖的值。但是,一旦用“del”删除了新值,覆盖的值就不再存在于实例中,因此查找会上升到更高的级别并从类中获取它。
使用类和实例数据
在本节中,让我们了解类数据如何与实例数据相关。我们可以将数据存储在类中或实例中。当我们设计一个类时,我们决定哪些数据属于实例,哪些数据应该存储到整个类中。
实例可以访问类数据。如果我们创建多个实例,那么这些实例可以访问它们各自的属性值以及整个类数据。
因此,类数据是在所有实例之间共享的数据。请观察下面给出的代码以更好地理解 -
class InstanceCounter(object): count = 0 # class attribute, will be accessible to all instances def __init__(self, val): self.val = val InstanceCounter.count +=1 # Increment the value of class attribute, accessible through class name # In above line, class ('InstanceCounter') act as an object def set_val(self, newval): self.val = newval def get_val(self): return self.val def get_count(self): return InstanceCounter.count a = InstanceCounter(9) b = InstanceCounter(18) c = InstanceCounter(27) for obj in (a, b, c): print ('val of obj: %s' %(obj.get_val())) # Initialized value ( 9, 18, 27) print ('count: %s' %(obj.get_count())) # always 3
输出
val of obj: 9 count: 3 val of obj: 18 count: 3 val of obj: 27 count: 3
简而言之,类属性对于类的所有实例都是相同的,而实例属性对于每个实例都是特定的。对于两个不同的实例,我们将有两个不同的实例属性。
class myClass: class_attribute = 99 def class_method(self): self.instance_attribute = 'I am instance attribute' print (myClass.__dict__)
输出
执行上面给出的代码时,您可以观察到以下输出 -
{'__module__': '__main__', 'class_attribute': 99, 'class_method':, '__dict__': , '__weakref__': , '__doc__': None}
实例属性myClass.__dict__如图所示 -
>>> a = myClass() >>> a.class_method() >>> print(a.__dict__) {'instance_attribute': 'I am instance attribute'}