Python中的类的理解(二)

写两个类,一个Person类,一个Manager类
编写构造函数
赋给实例属性第一个值的通常方法是,__init__构造方法中将他们赋值给self,构造函数方法包含了每次创建一个实例的时候Python会自动运行的代码。

#Add record field initialization
class Person:
    def __init__(self,name,job,pay):
        #Constructor takes 3 arguments
        self.name = name
        #Fill out fields when created
        self.job = job
        #self is the new instance object
        self.pay = pay

传入的数据作为构造函数方法的参数附加到一个实例上,并且将他们赋给self以保持持久。
在OO术语中,self就是新创建的实例对象,而name、job和pay变成了状态信息,即保存在对象中供随后使用的描述数据。
__init__其实没有什么奇妙的地方,当一个实例的时候,会自动调用它并且他有特殊的第一个参数。它仍然是一个常规特性。我们可以为它的参数提供默认值,从而当参数的值不可用的情况下,就不必提供参数值。
为了便于说明,使job参数成为可选的参数,他将默认为None,意味着所创建的人(目前)没有工作。如果job默认为None,使pay也默认为0,以保持一致性。事实上,我们必须为pay指定一个默认值,因为根据Python的语法规则,一个函数定义中,在第一个拥有默认值的参数之后的任何参数,都必须拥有默认值:

#Add default for constructor arguments
 class Person:
     def __init__(self,name,job=None,pay=0):
 #Normal function args
        self.name = name
        self.job = job
        self.pay = pay
     def giveRaise(self,percent):
         self.pay = int(self.pay*(1+percent))
     def __str__(self):#Added method  String to print
         return "[Person(人物): %s,%s]"%(self.name,self.pay)

这段代码意味着当我们创建Person的时候,需要给name传入值,但是job和pay现在是可选的,如果忽略这两个值的话,他们默认为None和0。通常self参数由Python自动填充以引用实例对象,把值赋给属性就会将赋值给新的实例。

#Test the class
sue = Person("Sue Jones",job="dev",pay=10000)#Runs __init__ automaticlly
print(bob.name,bob.pay)
print(sue.name,sue.pay)

从技术上讲,bob和sue都是命令空间对象,像所有的类实例一样,它们每一个都拥有各自类所创建的状态信息的独立副本。由类的每一个实例都有自己的一组self属性,类通过这种方式来记录多个对象的信息是再自然不过的事情了,就像是内置类型一样,类充当一种对象工厂。
运算符重载
除了__init__之后,__str__方法,每次一个实例转换为其打印字符串的时候,__str__都会自动运行。

class manager(Person):#Define a subclass of Person

这行代码意味着我们定义了一个名为Manager的新类,它继承自超类Person并且可能向Person添加一些定制。

#扩展方法:不好的方式
class Manager(Person):#Inherit Person attrs
	def giveRaise(self,percent,bonus=.10):	#Redefine to custmize
		self.pay = int(self.pay*(1+percent+bonus))	#Bad:cut-and-paste
#扩展方法:好的方式
class Manager(Person):
	def giveRaise(self,percent,bonus=.10):
		Person.giveRaise(self,percent+bbonus)

这段代码利用了一个事实:类方法总是可以在一个实例中调用,或通过类来调用。
下列二者相同:instance.method(args…) == class.method(instance,args…)
我们想要以为Manager定制构造函数逻辑的方式来自动提供一个的工作名。涉及代码,我们想要重新定义Manager中__init__方法,从而提供mgr字符串。而且和giveRaise的定制一样,我们还想通过类名的调用来运行Person中最初的__init__,以便它仍然会初始化对象的状态信息属性。

#Add customization of constructor in a subclass
class Person:
	def __init__(self,name,job=None,pay=0):
		self.name = name
		self.job = job
		self.pay = pay
	def lastname(self):
		return self.name.split()[-1]
	def giveRaise(self,percent):
		self.pay = int(self.pay * (1 + percent))
	def __str__(self):
		return '[Person: %s,%s]'%(self.name,self.pay)
class Manager(Person):
	def __init__(self,name,pay):
		Person.__init__(self,name,'mgr',pay)
	def giveRaise(self,percent,bonus=.10):
		Person.giveRaise(self,percent + bonus)

if __name__ == '__main__':
	bob = Person('Bob Smith')
	sue = Person('Sue Jones',job='dev',pay=100000)
	print(bob)
	print(sue)
	print(bob.lastname(),sue.lastname())
	sue.giveRaise(.10)
	print(sue)
	tom = Manager('Tom Jones',50000)
	tom.giveRaise(.10)
	print(tom.lastName())
	print(tom)

class语句

class语句是对象的创建者并且是一个隐含的赋值运算——执行时,它会产生类对象,并把其引用值存储在前面所使用的变量名。此外,像def一样,class语句也是真正的可执行代码。直到Python抵达并运行定义的class语句前,你的类都不存在(一般都是在其所在模块被导入时,在这之前都不会存在)。

1> 一般形式
class是符合语句,其缩进语句的主体一般都出现在头一行下边。在头一行中,超类列在类名称之后的括号内,由逗号相隔。列出一个以上的超类会引起多重继承

class <name>(superclass,...):
	data = value
	def method(self,...):
	self.mber = value

在class语句内,任何赋值语句都会产生类属性,而且还有特殊名称方法重载运算符。例如名为__init__的函数会在实例对象构造时调用

方法

1>.方法位于class语句的主体内,是由def语句建立的函数对象。从抽象的视角来看,方法替实例对象提供了要继承的行为。从程序设计来看,方法的工作方式与简单函数完全一致,只是有个重要差异:方法的第一个参数总是接受方法调用的隐性主体,也就是实例对象。
换句话说,Python会自动把实例方法的调用对应到类方法函数,如下所示:

instance.method(args...)

这会自动翻译成以下形式的类方法函数调用:

class.method(instance,args...)

除了方法属性名称是正常的继承外,第一个参数就是方法调用背后唯一的神奇之处。在类方法中,按惯例第一个参数通常通常称为self(严格说,只有其位置重要,而不是它的名称)。这个参数给方法提供了一个钩子,从而返回调用的主体,也就是实例对象:因为类可以许多实例对象,所以需要这个参数来和管理每个实例彼此各不相同的数据。
在python中,self一定要在程序代码中明确地写出:方法一定要通过self来取出或修改由当前方法调用或正在处理的实例的属性。这种让self明确化的本质是有意设计的:这个变量名存在,会让你明确脚本中使用的是实例属性名称,而不是本地作用域或全局作用域的变量名。
如下:

class NextClass:	#Define class
	def printer(self,text):
		self.message = text
		print(self.message)

变量名printer引用了一个函数对象。因为这是class语句的作用域中赋值,就会变成类对象的属性,被由这个类创建的每个实例所继承。通过实例予以调用。

x = NextClass()
x.printer('instance call')
x.message

当通过对实例进行点号运算调用它时,printer会先通过继承将其定位,然后self参数会自动赋值为实例对象(x)。text参数会获得在调用时传入的字符串(‘instance call’)。
方法能通过实例或类本身两种方法其中的任意一种进行调用。可以通过类的名称调用printer,只要明确地传递一个实例给self参数。

NextClass。printer(x,'class call')
x.message

2>.调用超类构造函数
方法一般是通过实例调用的。不过,通过类调用方法也扮演一些特殊的角色。常见的场景涉及了构造函数。就像所有属性__init__方法是由继承进行查找的。也就是说,在构造时,Python会找出并且只调用一个__init__。如果要保证子类的构造函数也会执行超类构造时的逻辑,一般都必须通过类明确地调用超类的__init__方法。

class Super:
	def __init__(self,x):
		...default code...
class sub(Super):
	def __init__(self,x,y):
		Super.__init__(self,x)
		...custom code...	

这是代码有可能直接调用运算符重载方法的环境之一,如果真的想运行超类的构造方法,自然只能用这种方式进行调用:没有这样的调用,子类会完全取代超类的构造函数。

继承

像class语句这样的命名空间工具的重点就是变量名继承。
在Python中,当对对象进行点号运算时,就会发生继承,而且涉及了搜索属性定义树(一个或多个命名空间)。每次使用object.attr形式的表达式时(object是实例或类对象),Python会从头至尾搜索命名空间,先从对象开始,寻找所能找到的第一个attr。这包括在方法中对self属性的引用。因为树中较低的定义会覆盖较高的定义,继承构成了专有化基础。

class Super:
    def method(self):
        print("in Super.method")
    def delegate(self):
        self.action()

class Inheritor(Super):
    pass

class Replacer(Super):
    def method(self):
        print("in Replacer.method")
class Extender(Super):
    def method(self):
        print('starting Extender.method')

class Provider(Super):
    def action(self):
        print('in Provider.action')

class Sub(Super):
    def method(self):
        print('starting Sub.method')
        Super.method(self)
        print('ending Sub.method')

if __name__ == '__main__':
    for klass in (Inheritor,Replacer,Extender):
        print('\n'+klass.__name__+'...')
        klass().method()
        print('Provider...')
        x = Provider()
        x.delegate()

注:这个例子末尾的自我测试程序代码会在for循环中建立三个不同类实例。因为类是对象,你可将他们放在元组中并且通过通用方式创建实例。类也有特殊的__name__属性,就想模块。默认为类首行中的类名称的字符串。

注意上面例子中的Provider类是如何工作的。当通过Provider实例调用delegate方法时,有两个独立的继承搜索会发生:
1.在最初x.delegate的调用中,Python会搜索Provider实例和它上层的对象,知道在Super中找到delegate的方法。实例x会像往常一样传递给这个方法的self参数。
2.在Super.delegate方法中,self.action会对self以及它上层的对象启动新的独立继承搜索。因为self指的是Provider实例,在Provider子类中会找到action方法。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值