class随笔(二)

---恢复内容开始---

类和实例

类对象来自于语句,而实例来自于调用。每次调用一个类,就会得到这个类的新的实例。

  • class语句创建类对象并将其赋值给变量名。class语句是可执行语句,执行时会产生新的类对象,并将其赋值给class头部的变量名。
  • class语句内的赋值语句会创建类的属性。
  • 类属性提供对象的状态和行为。类中的def函数会生成方法,方法将会处理实例。

当调用类对象时,就可以生成实例对象。

  • 像函数那样调用类对象会创建新的实例对象。
  • 每个实例对象继承类的属性并获得了自己的命名空间。
  • 在方法内对self属性对赋值运算会产生每个实例自己的属性。在类方法函数内,第一个参数(即self)会引用正处理的实例对象。对self的属性做赋值运算,会创建或修改实例内的数据,而不是类的数据。

例1:

class FirstClass:    # define a class object
    def setdata(self, value):    # define class methods
        self.data = value    # self is the instance
    def display(self):
        print(self.data)    # self.data: per instance

代码分析:

  • class语句开头一行会列出类的名称,后面再接一个或多个内嵌并且缩进的语句的主体。(在这里,嵌套的语句是def,定义类要实现导出的行为的函数)
  • def其实是赋值运算,此处是将函数对象赋值给变量名setdata,而且display位于class语句范围内,因此会产生附加在类上的属性:FirstClass.setdata 和 FirstClass.display。在类嵌套的代码块中顶层的赋值的任何变量名,都会变成类的属性。
  • 位于类中的函数通常称为方法,方法是普通def,在方法函数中,调用时,第一个参数自动接收隐含的实例对象:调用的主体。

用一些实例来解释这个类:

x = FirstClass()
y = FirstClass()

以这种方式调用类时(注意小括号),会产生实例对象,也就是可读取类属性的命名空间。确实地讲,此时有三个对象:两个实例和一个类。以OOP的观点来看,x是一个FirstClass对象,y也是。命名空间如下:

X.data -----------------FirstClass.setdata FirstClass.display

Y.data------------------FirstClass.setdata FirstClass.display

data属性会在实例内找到,但setdata和display则是在它们之上的类中找到。如果对实例以及类对象内的属性名称进行点号运算,python会通过继承搜索从类取得变量名:

x.setdata('King Arthur')    # call methods: self is x

x本身没有setdata属性,python在调用这个方法时,会顺着实例到类的连接搜索,这就是继承。继承是在属性点号运算时发生的,而且只与查找连接对象内的变量名有关。

在FirstClass的setdata函数中,传入的值会赋给self.data,在方法中,self会自动引用正在处理的实例(x),所以赋值语句会把值储存在实例的命名空间,而不是类的命名空间。

在类内时,通过方法内对self进行赋值运算,在类外时,通过对实例对象进行赋值运算。(通过在类方法函数外对变量名进行赋值运算,我们甚至可以在实例外命名空间内产生全新的属性)。

类通常是对self参数进行赋值运算从而建立实例的所有属性的,但不是必须如此。

逻辑的修改是通过创建子类,而不是修改超类。 

 

例2:

class SecondClass(FirstClass):    #从FirstClass继承了setdata
    def display(self):    # 在子类中修改了display
        print('Current value = %s' % self.data)

因为子类SecondClass中重新定义了display方法,而继承搜索会从实例往上执行,之后到子类,再之后到超类,直到所找的属性名称首次出现为止,所以SecondClass中的display会覆盖掉FirstClass中的display。这种在树中较低处发生的重新定义的、取代属性的动作称为重载。

z = SecondClass()
z.setdata(42)
z.display()    # prints ''Current value = '42'''

其中:z.data从实例z中找到,z.display从子类SecondClass中找到,z.setdata从超类FirstClass中找到。

而SecondClass引入的专有化完全是在FirstClass外部完成的,所以不会影响存在的或未来修改的FirstClass对象。所以类所支持的扩展和重用比函数或模块更好。

 

例3:

class ThirdClass(SecondClass):
    def __init__(self, value):    # on 'ThirdClass(value)'
        self.data = value
    def __add__(self, other):    # on 'self + other'
        return ThirdClass(self.data + other)
    def __str__(self):    # on 'print(self)', 'str()'
        return '[ThirdClass: %s]' % self.data
    def mul(self, other):    # in-place change: names
        self.data *= other

a = ThirdClass('abc')    # 调用__init__
a.display()    # 从SecondClass继承display方法,打印出'Current value = 'abc''
print(a)    # 调用__str__ 返回'[ThirdClass: abc]'
b = a + 'xyz'    # 调用__add__,并产生一个新的实例b
b.display()    # 新实例b拥有ThirdClass的所有方法,打印出'Current value = 'abcxyz''
print(b)    # 调用__str__,返回'[ThirdClass: abcxyz]'
a.mul(3)    # 原地修改实例
print(a)    # 返回'[ThirdClass: abcabcabc]'

代码分析: ThirdClass是一个SecondClass对象,所以其实例会继承SecondClass的display方法,但是ThirdClass生成的调用会传递一个参数给__init__构造函数中的value,并将其赋值给self.data。直接效果是ThirdClass计划在构建时自动设置data属性,而不是在构建之后请求setdata调用。

此外,ThirdClass对象现在出现在‘+’表达式的print调用中,对于‘+’, python把左侧的实例对象传给__add__中的self参数,而把右边的值传给other。对于print,python把要打印的对象传递给__str__中的self,该方法返回的字符串看做是对象的打印字符串。

最常见的重载方法是__init__构造函数,这个函数可以让类立即在新建的实例内添加属性。

 

例4:

class rec: pass    # empty namespace object

rec.name = 'Bob'    # 在class语句外,通过变量赋值变量名给类增加属性
rec.age = 40

print(rec.name)    # 返回 Bob 。此时类还没有实例,类本身也是对象,是独立的命名空间。

x = rec()
y = rec()    # 建立两个实例
x.name
y.name    # 返回'Bob'。因继承获取附加在类上的属性

x.name = 'sue'    # 如果把一个属性赋值给一个实例,就会在该对象内修改这个属性。
rec.name    #  'Bob'
x.name     # 'sue'
y.name    # 'Bob' 属性赋值运算只会影响属性赋值所在的对象,所以x得到自己的name,而y依然继承附加在它的类上的name

类的__x__内部名称集合:

rec.__dict__.keys()    # ['__module__', 'name', 'age', '__dict__', '__weakref__', '__doc__']

list(x.__dict__.keys())    # ['name']
list(y.__dict__.keys())    # []

x.__class__    # 查看x的连接所继承的类<class '__main__.rec'>
rec.__bases__    # 显示rec的超类的元组(<class 'object'>,)

即使是方法(通常是在类中通过def创建)也可以独立地在任意类对象的外部创建:

def upperName(self):
    return self.name.upper()    # 需要传入一个self参数

upperName(x)    # 传入一个带有name属性的x,返回'sue'

若把这个函数的赋值成类的属性,就会变成方法,可以由任何实例调用:

rec.method = upperName
x.method()    # 返回'sue'。调用方法处理x
y.method()    # 返回'Bob'
rec.method(x)    # 返回'sue'。

 

总结:

  • 类属性的创建是通过把属性赋值给类对象实现的,通常是由class语句中的顶层赋值语句产生(如class中def的setdata或display等,setdata等即成为了类属性),也可以于任何引用类对象的地方对其属性赋值创建类属性。
  • 实例属性的创建是通过对实例对象赋值属性来创建的,通常是在class语句中对self参数赋值属性而创建的(如setdata中的self.name = name,name即成为了实例属性),也可以在外部引用实例参过赋值语句来创建属性。一般来说,所有实例属性都是在__init__构造函数中初始化的。
  • self通常是给与类方法函数中的第一个参数的名称,python会自动填入实例对象。
  • 运算符重载是通过__x__这种特定名称的方法实现的。常见的__init__构造函数与__str__显示函数。

 

转载于:https://www.cnblogs.com/calvinsun/p/7138201.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值