如果说类是一种数据结构的定义,那么实例就是声明了一个该类型的变量.在Python3.x中默认所有的类都为新式类,类型的实例就是这种类型的对象,定义一个新的类,即创建了一个新的类.
创建实例
在大部分面向对象的语言中,提供了关键字new来创建类的实例.Python更加简单,通过跟函数调用的形式来完成类的初始化和实例的创建.
class MyClass():
pass
# 初始化类并创建实例
mc = MyClass()
_init_()方法(构造器constructor)
创建实例时,类会被调用创建类的实例对象,然后Python会检查是否实现了_init_(),默认情况下,如果没有重写_init_()的话,则执行默认的操作,返回一个默认情况下的实例.如果需要对实例增加一些初始的数据属性的操作,就需要重写(覆盖)_init_()方法.
如果在类的定义中重写了特殊方法_init_(),在创建类的实例后会被调用,实例对象作为第一个参数self被传入_init_(),完成类的初始化和实例创建._init_()方法是类定义的特殊方法之一,在缺省情况下,不进行任何操作,如果需要定制一些类的属性,就要重写特殊方法,覆盖它的的默认行为.
_new_()方法(构造器)
类的特殊方法_new_()[静态方法]其实跟特殊方法init()功能类似,都是在需要对类的实例增加一些特定的操作时要通过重写这些特殊方法来实现,不同点是:init()方法是对从object基类继承的类使用的特殊方法._new_()方法是当用户要对Python的内建类型进行派生是用到的特殊方法.比如说要对字符串类型,浮点类型数据进行派生时,要用到_new_()方法类初始化类._new_()方法会调用父类的_new_()创建对象.
_del_()方法(解构器destructor)
特书方法_del_()很少被重写(覆盖),因为实例对象大都是被隐式的释放掉,因为Python的垃圾对象回收机制是通过引用计数来实现的,当一个实例所有的引用都被清除后,_del_()方法就会被调用.
class P():
def __del__(self):
print('deleted')
class L(P):
def __init__(self):
print('initialized')
def __del__(self):
P.__del__(self) # 调用父类的__del__方法
# 初始化,创建实例,打印initialized,调用__init__()
c1 = L()
# 别名对实例的引用
c2 = c1
c3 = c1
# 内建函数id()来确定不同别名是对同一个对象的引用
print(id(c1),id(c2),id(c3))
initialized
139950121340488 139950121340488 139950121340488
del c1
del c2
del c3 # 在对类的实例对象引用计数归零的时候,__del__()被调用
deleted
总结:
解构器只会被调用一次,一旦引用计数为0,对象就会被清除.
从父类派生出的新类,在重写解构器的时候,应该首先调用父类的解构器.
调用del c1不表示调用了_del_(),_del_()总是隐式的被调用.
如果对一个实例对象进行循环引用,导致计数不断增加,_del_()将不会被调用.
_del_()未捕获的异常可能会被忽略,在解构器被调用的时候有些变量可能已经被删除了.
解构器如果被重写了,并且实例是某个循环的一部分,垃圾回收机制将不会终止这个循环,需要显示的调用解构器
跟踪实例
在Python中虽然有对象引用的计数机制,但是没对实例创建的个数计数,所以要想知道一个类的多少个实例被创建了,需要手动的增加这个功能,可以通过覆盖_init_(),_del_()方法来实现.使用一个静态的类成员变量来计数.
class InstCount():
count = 0 #类属性
# 每当创建一个实例,类属性count就+1
def __init__(self):
InstCount.count += 1
def __del__(self):
InstCount.count -=1
# 返回实例数量
def howMany(self):
return InstCount.count
a1 = InstCount()
a2 = InstCount()
a3 = InstCount()
a1.howMany()
3
del a2
print('InsCount.count=%d, a1.howMany():%d'%(InstCount.count, a1.howMany()))
InsCount.count=2, a1.howMany():2
del a3
del a1
print('InsCount : %d'%InstCount.count)
InsCount : 0
实例属性
实例的方法属性是从父类继承,数据属性既可以从父类继承,也可以在实例创建后任意设置.还可以在类定义中在_init_()中设置实例的属性.在静态类型的语言中所有的属性在使用前都要有明确的声明和定义.作为动态语言Python中,实例的属性可以动态的创建.这动态语言的优点同时也是它的一个缺陷.__如果属性在条件语句中被创建,条件语句可能不会被执行,属性也就不存在,如果在后面的代码中访问个这个属性就会抛出Attribute异常,需要谨慎.
在构造器中设置实例属性
在实例创建时第一个调用的方法是_init_(),构造器执行完毕后返回实例对象,完成实例化.实例对象是自动在实例化后返回的.所以构造器不返回任何对象.在构造器中和普通的函数一样可以设置默认参数,这样在创建实例的时候就不需要显式给构造器传递值了.但是在需要改变数据属性的时候也可以通过显式的传递新的值来覆盖默认值.
下面是一个租房的例子,假设某地租房要交8.5%的销售税,10%的房间税.每天的房租不舍默认值.
class HotelRoomCalc():
def __init__(self, rt, sales=0.085, rm=0.1):
self.roomRate = rt
self.salesTax = sales
self.roomTax = rm
def calcTotal(self, days=1):
daily = round(self.roomRate*(1+self.salesTax+self.roomTax), 2)
return float(days)*daily
# 日租金
f1 = HotelRoomCalc(299)
f1.calcTotal()
354.31
f1.calcTotal(100)
35431.0
# 新实例,另一个地方的房租情况
f2 = HotelRoomCalc(200, 0.25, 0.15)
f2.calcTotal()
280.0
实例属性和类属性都可以通过内建函数dir()打印所有的实例属性:
print(dir(f1))
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'calcTotal', 'roomRate', 'roomTax', 'salesTax']
实例也'继承'了类的_dict_属性
print(f1.__dict__) # vars()内建函数也支持实例
{'roomRate': 299, 'salesTax': 0.085, 'roomTax': 0.1}
修改__dict_
对于类和实例,_dict_属性是可修改的,但是不建议这样做,除非有非常明确的目的.这写修改可能会破坏OOP,造成一些副作用.如果非要修改_dict_,还要重写_setattr_()方法来实现,覆盖setattr方法本身就是很冒险的.例如无穷的递归和破坏实例对象.
内建类型和实例:
内建类型就是Python自带的数据类型,列表,字典,浮点数,整数,浮点数,复数等等.内建类型和自定义类型一样,可以同过dir()来查看类实例属性,内建类型没有_dict_属性
# 内建类型复数实例
x = 2+0.3j
type(x), x.__class__
(complex, complex)
print(dir(x))
['__abs__', '__add__', '__bool__', '__class__', '__delattr__', '__dir__', '__divmod__', '__doc__', '__eq__', '__float__', '__floordiv__', '__format__', '__ge__', '__getattribute__', '__getnewargs__', '__gt__', '__hash__', '__init__', '__int__', '__le__', '__lt__', '__mod__', '__mul__', '__ne__', '__neg__', '__new__', '__pos__', '__pow__', '__radd__', '__rdivmod__', '__reduce__', '__reduce_ex__', '__repr__', '__rfloordiv__', '__rmod__', '__rmul__', '__rpow__', '__rsub__', '__rtruediv__', '__setattr__', '__sizeof__', '__str__', '__sub__', '__subclasshook__', '__truediv__', 'conjugate', 'imag', 'real']
print("x's imag:%.1f, x's real:%.1f"%(x.imag, x.real))
x's imag:0.3, x's real:2.0
# 共轭
print("x's conjugate:",x.conjugate())
x's conjugate: (2-0.3j)
实例属性.类属性
实例属性是动态属性,可以在任何时候增加,删除,修改,类的数据属性像静态成员一样被引用,在多次实例化中类数据属性不变.实例可以访问类属性,不改变类的属性,修改类属性需要类名.如果实例有同名属性会覆盖类属性,此时可以通过类名来访问类属性.
class C():
x = 1
c1 = C()
c1.x = 2
# 覆盖类属性
c1.x
2
del c1.x
c1.x # 有可以访问到类属性
1
# 类属性改变,实例也改变
C.x = 3
c1.x
3
类属性可变的情况
(类属性是一个字典类型)
class P():
d = {1:'dog'}
c = P()
c.d
{1: 'dog'}
c.d[2]='cat'
c.d
{1: 'dog', 2: 'cat'}
P.d # 类属性被改变了
{1: 'dog', 2: 'cat'}
del c.d
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-80-07492612cbc3> in <module>()
----> 1 del c.d
AttributeError: d
AttributeError是因为实例c并没有创建同名的属性,只是从类继承的属性