Classes/Objects
Python一直是面向对象的语言,因为它存在。 因此,创建和使用类和对象非常简单。 本章帮助您成为使用Python面向对象编程支持的专家。
如果您之前没有任何面向对象(OO)编程的经验,您可能需要参考它的入门课程或至少某种类型的教程,以便掌握基本概念。
但是,这里是面向对象编程(OOP)的小型介绍,以加快您的速度 -
OOP术语概述
Class - 用户定义的对象原型,它定义了一组表征类的任何对象的属性。 属性是数据成员(类变量和实例变量)和方法,可通过点表示法访问。
Class variable - 由类的所有实例共享的变量。 类变量在类中定义,但在类的任何方法之外。 类变量的使用频率与实例变量不同。
Data member - 包含与类及其对象关联的数据的类变量或实例变量。
Function overloading - 为特定函数分配多个行为。 执行的操作因所涉及的对象或参数的类型而异。
Instance variable - 在方法内定义的变量,仅属于类的当前实例。
Inheritance - 将类的特征传递给从其派生的其他类。
Instance - 某个类的单个对象。 例如,属于Circle类的对象obj是Circle类的实例。
Instantiation - 创建类的实例。
Method - 在类定义中定义的一种特殊函数。
Object - 由其类定义的数据结构的唯一实例。 对象包括数据成员(类变量和实例变量)和方法。
Operator overloading - 为特定运算符分配多个函数。
创建类
class语句创建一个新的类定义。 该类的名称紧跟在关键字class之后,后跟冒号,如下所示 -
class ClassName:
'Optional class documentation string'
class_suite
该类有一个文档字符串,可以通过ClassName.__doc__访问。
class_suite由定义类成员,数据属性和函数的所有组件语句组成。
例子 (Example)
以下是一个简单的Python类的示例 -
class Employee:
'Common base class for all employees'
empCount = 0
def __init__(self, name, salary):
self.name = name
self.salary = salary
Employee.empCount += 1
def displayCount(self):
print "Total Employee %d" % Employee.empCount
def displayEmployee(self):
print "Name : ", self.name, ", Salary: ", self.salary
变量empCount是一个类变量,其值在此类的所有实例之间共享。 这可以从类内部或类外部以Employee.empCount形式访问。
第一个方法__init__()是一个特殊的方法,它被称为类构造函数或初始化方法,当您创建此类的新实例时,Python会调用它们。
您声明其他类方法,如普通函数,但每个方法的第一个参数是self 。 Python将self参数添加到列表中; 调用方法时不需要包含它。
创建实例对象
要创建类的实例,可以使用类名调用该类,并传入__init__方法接受的任何参数。
"This would create first object of Employee class"
emp1 = Employee("Zara", 2000)
"This would create second object of Employee class"
emp2 = Employee("Manni", 5000)
访问属性
您可以使用带有对象的点运算符来访问对象的属性。 可以使用类名访问类变量,如下所示 -
emp1.displayEmployee()
emp2.displayEmployee()
print "Total Employee %d" % Employee.empCount
现在,将所有概念放在一起 -
#!/usr/bin/python
class Employee:
'Common base class for all employees'
empCount = 0
def __init__(self, name, salary):
self.name = name
self.salary = salary
Employee.empCount += 1
def displayCount(self):
print "Total Employee %d" % Employee.empCount
def displayEmployee(self):
print "Name : ", self.name, ", Salary: ", self.salary
"This would create first object of Employee class"
emp1 = Employee("Zara", 2000)
"This would create second object of Employee class"
emp2 = Employee("Manni", 5000)
emp1.displayEmployee()
emp2.displayEmployee()
print "Total Employee %d" % Employee.empCount
执行上述代码时,会产生以下结果 -
Name : Zara ,Salary: 2000
Name : Manni ,Salary: 5000
Total Employee 2
您可以随时添加,删除或修改类和对象的属性 -
emp1.age = 7 # Add an 'age' attribute.
emp1.age = 8 # Modify 'age' attribute.
del emp1.age # Delete 'age' attribute.
您可以使用以下函数 - 而不是使用常规语句来访问属性 -
getattr(obj, name[, default]) - 访问object的属性。
hasattr(obj,name) - 检查属性是否存在。
setattr(obj,name,value) - 设置属性。 如果属性不存在,则会创建它。
delattr(obj, name) - 删除属性。
hasattr(emp1, 'age') # Returns true if 'age' attribute exists
getattr(emp1, 'age') # Returns value of 'age' attribute
setattr(emp1, 'age', 8) # Set attribute 'age' at 8
delattr(empl, 'age') # Delete attribute 'age'
内置类属性
每个Python类都遵循内置属性,并且可以使用点运算符访问它们,就像任何其他属性一样 -
__dict__ - 包含类名称空间的字典。
__doc__ - 类文档字符串或无,如果未定义。
__name__ - class名称。
__module__ - 定义类的模块名称。 此属性在交互模式下为“__main__”。
__bases__ - 一个可能为空的元组,包含基类,按它们在基类列表中出现的顺序排列。
对于上面的类,让我们尝试访问所有这些属性 -
#!/usr/bin/python
class Employee:
'Common base class for all employees'
empCount = 0
def __init__(self, name, salary):
self.name = name
self.salary = salary
Employee.empCount += 1
def displayCount(self):
print "Total Employee %d" % Employee.empCount
def displayEmployee(self):
print "Name : ", self.name, ", Salary: ", self.salary
print "Employee.__doc__:", Employee.__doc__
print "Employee.__name__:", Employee.__name__
print "Employee.__module__:", Employee.__module__
print "Employee.__bases__:", Employee.__bases__
print "Employee.__dict__:", Employee.__dict__
执行上述代码时,会产生以下结果 -
Employee.__doc__: Common base class for all employees
Employee.__name__: Employee
Employee.__module__: __main__
Employee.__bases__: ()
Employee.__dict__: {'__module__': '__main__', 'displayCount':
<function displayCount at 0xb7c84994>, 'empCount': 2,
'displayEmployee': <function displayEmployee at 0xb7c8441c>,
'__doc__': 'Common base class for all employees',
'__init__': <function __init__ at 0xb7c846bc>}
Destroying Objects (Garbage Collection)
Python会自动删除不需要的对象(内置类型或类实例)以释放内存空间。 Python定期回收不再使用的内存块的过程称为垃圾收集。
Python的垃圾收集器在程序执行期间运行,并在对象的引用计数达到零时触发。 对象的引用计数随指向它的别名数量的变化而变化。
对象的引用计数在分配新名称或放置在容器(列表,元组或字典)中时会增加。 当删除del时,对象的引用计数减少,其引用被重新分配,或者引用超出范围。 当对象的引用计数达到零时,Python会自动收集它。
a = 40 # Create object <40>
b = a # Increase ref. count of <40>
c = [b] # Increase ref. count of <40>
del a # Decrease ref. count of <40>
b = 100 # Decrease ref. count of <40>
c[0] = -1 # Decrease ref. count of <40>
您通常不会注意到垃圾收集器何时销毁孤立实例并回收其空间。 但是类可以实现特殊方法__del__() ,称为析构函数,在实例即将被销毁时调用。 此方法可用于清除实例使用的任何非内存资源。
例子 (Example)
这个__del __()析构函数打印一个即将被销毁的实例的类名 -
#!/usr/bin/python
class Point:
def __init__( self, x=0, y=0):
self.x = x
self.y = y
def __del__(self):
class_name = self.__class__.__name__
print class_name, "destroyed"
pt1 = Point()
pt2 = pt1
pt3 = pt1
print id(pt1), id(pt2), id(pt3) # prints the ids of the obejcts
del pt1
del pt2
del pt3
执行上面的代码时,会产生以下结果 -
3083401324 3083401324 3083401324
Point destroyed
Note - 理想情况下,您应该在单独的文件中定义类,然后您应该使用import语句将它们导入主程序文件中。
类继承
您可以通过在新类名后面的括号中列出父类,而不是从头开始,通过从预先存在的类派生它来创建类。
子类继承其父类的属性,您可以使用这些属性,就好像它们是在子类中定义的一样。 子类还可以覆盖父级的数据成员和方法。
语法 (Syntax)
派生类的声明与其父类非常相似; 但是,在类名后面给出了一个要继承的基类列表 -
class SubClassName (ParentClass1[, ParentClass2, ...]):
'Optional class documentation string'
class_suite
例子 (Example)
#!/usr/bin/python
class Parent: # define parent class
parentAttr = 100
def __init__(self):
print "Calling parent constructor"
def parentMethod(self):
print 'Calling parent method'
def setAttr(self, attr):
Parent.parentAttr = attr
def getAttr(self):
print "Parent attribute :", Parent.parentAttr
class Child(Parent): # define child class
def __init__(self):
print "Calling child constructor"
def childMethod(self):
print 'Calling child method'
c = Child() # instance of child
c.childMethod() # child calls its method
c.parentMethod() # calls parent's method
c.setAttr(200) # again call parent's method
c.getAttr() # again call parent's method
执行上述代码时,会产生以下结果 -
Calling child constructor
Calling child method
Calling parent method
Parent attribute : 200
类似地,您可以从多个父类驱动一个类,如下所示 -
class A: # define your class A
.....
class B: # define your class B
.....
class C(A, B): # subclass of A and B
.....
您可以使用issubclass()或isinstance()函数来检查两个类和实例的关系。
如果给定的子类sub确实是超类sup的子类,则issubclass(sub, sup)布尔函数返回true。
如果obj是类Class的实例或者是Class的子类的实例,则isinstance(obj, Class)布尔函数返回true
重写方法
您始终可以覆盖父类方法。 覆盖父方法的一个原因是因为您可能需要子类中的特殊功能或不同功能。
例子 (Example)
#!/usr/bin/python
class Parent: # define parent class
def myMethod(self):
print 'Calling parent method'
class Child(Parent): # define child class
def myMethod(self):
print 'Calling child method'
c = Child() # instance of child
c.myMethod() # child calls overridden method
执行上述代码时,会产生以下结果 -
Calling child method
基础重载方法
下表列出了一些您可以在自己的类中覆盖的通用功能 -
Sr.No. | 方法,描述和样本调用 |
---|---|
1 | __init__ ( self [,args...] ) 构造函数(带有任何可选参数) 示例调用: obj = className(args) |
2 | __del__( self ) 析构函数,删除一个对象 样本调用: del obj |
3 | __repr__( self ) 可评估的字符串表示 样本电话: repr(obj) |
4 | __str__( self ) 可打印的字符串表示 样本调用: str(obj) |
5 | __cmp__ ( self, x ) 对象比较 样本调用: cmp(obj, x) |
重载运算符
假设您已经创建了一个Vector类来表示二维向量,当您使用加号运算符添加它们时会发生什么? 很可能Python会对你大喊大叫。
但是,您可以在类中定义__add__方法以执行向量添加,然后加号运算符将按预期运行 -
例子 (Example)
#!/usr/bin/python
class Vector:
def __init__(self, a, b):
self.a = a
self.b = b
def __str__(self):
return 'Vector (%d, %d)' % (self.a, self.b)
def __add__(self,other):
return Vector(self.a + other.a, self.b + other.b)
v1 = Vector(2,10)
v2 = Vector(5,-2)
print v1 + v2
执行上述代码时,会产生以下结果 -
Vector(7,8)
数据隐藏
对象的属性在类定义之外可能可见也可能不可见。 您需要使用双下划线前缀命名属性,然后这些属性不会被外人直接看到。
例子 (Example)
#!/usr/bin/python
class JustCounter:
__secretCount = 0
def count(self):
self.__secretCount += 1
print self.__secretCount
counter = JustCounter()
counter.count()
counter.count()
print counter.__secretCount
执行上述代码时,会产生以下结果 -
1
2
Traceback (most recent call last):
File "test.py", line 12, in <module>
print counter.__secretCount
AttributeError: JustCounter instance has no attribute '__secretCount'
Python通过内部更改名称来包含类名来保护这些成员。 您可以访问object._className__attrName等属性。 如果你将以下内容替换你的最后一行,那么它适合你 -
.........................
print counter._JustCounter__secretCount
执行上述代码时,会产生以下结果 -
1
2
2