在本章中,我们将详细讨论面向对象的术语和编程概念。类只是一个实例的工厂。 该工厂包含描述如何制作实例的蓝图。 一个实例或对象是从该类构造而来的。 在大多数情况下,我们可以有一个以上的类实例。 每个实例都有一组属性,这些属性在一个类中定义,因此每个特定类的每个实例都应该具有相同的属性。
类包:行为和状态
一个类将允许将对象的行为和状态捆绑在一起。 观察下图以更好地理解 -
讨论类包时,以下几点需要注意 -
- 行为(behavior)与函数相同 - 它是一段执行某些操作(或实现行为)的代码,
- 状态(state)与变量相同 - 它是一个在类中存储值的地方。
- 当声明一个类的行为和状态时,它是一个类包函数和变量。
类具有方法和属性
在Python中,创建方法定义了一个类行为。 方法是在一个类中定义的函数提供的OOP名称。 归纳如下 -
- 类函数 - 是方法的同义词
- 类变量 - 是名称属性的同义词。
- 类 - 具有确切行为的实例的蓝图。
- 对象 - 类的一个实例,执行类中定义的功能。
- 类型 - 表示实例所属的类
- 属性 - 任何对象值:
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使我们能够以下列方式隐藏对开发人员有利的对象内部工作的复杂性 -
- 简化并使得在不知道内部结构的情况下使用对象变得容易理解。
- 任何更改都可以很容易地管理。
面向对象编程在很大程度上依赖于封装。术语封装和抽象(也称为数据隐藏)通常用作同义词。 它们几乎是同义词,因为抽象是通过封装来实现的。
封装提供了限制访问某些对象组件的机制,这意味着对象的内部表示无法从对象定义的外部看到。 访问这些数据通常是通过特殊方法来实现的 - 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'}