Python学习杂记

学习资料:《Python核心编程(第二版)》

第13章 面向对象编程

13.4 类属性

  • 数据属性、函数属性
  • 类属性仅与其被定义的类相绑定,而实例数据属性用得更多;类数据属性仅当需要有更加“静态”数据类型时才变得有用

13.4.1 类的数据属性

class C(object):
     foo = 100

13.4.2 Methods

  • 方法:直接调用 methodname() 会引发 NameError 异常,因为它不在全局名字空间中;用类对象调用此方法会引发 TypeError 异常
  • 绑定:方法必须绑定到一个实例才能直接被调用

13.4.3 决定类的属性

  • dir() , __dict__, vars()

13.4.4 特殊的类属性

  • C.__name__
  • C.__doc__
  • C.__bases__
  • C.__dict__
  • C.__module__
  • C.__class__

__name__ 是给定类的字符名字,它适用于那种只需要字符串(类对象的名字),而非类对象本身的情况。內建的类型也有这个属性。

  • type() 返回被调用对象的类型。
  • '123' 是str类型对象,str是type对象
>>> type('123')
... <type 'str'>
>>> type('123').__name__
... 'str'
>>> type(type('123'))
... <type 'type'>

13.5 实例

13.5.1 初始化:通过调用类对象来创建实例

>>> class MyClass(object):
...     pass
...
>>> mc = MyClass()

13.5.2 __init__() “构造器”方法

13.5.3 __new__() “构造器”方法

13.5.4 __del__() “解构器”方法

  • 由于Python具有垃圾对象回收机制(靠引用计数),这个方法要直到该实例对象所有的引用都被清除掉后才会执行。
  • Python中的解构器是在实例释放前提供特殊处理功能的方法,通常没有被实现,因为实例很少被显式释放
  • 如果有一个循环引用或其他原因,让一个实例的引用逗留下去,该对象的 __del__() 可能永远不会执行
  • __del__() 未捕获的异常会被忽略掉
    跟踪实例:跟踪一个类有多少个实例被创建了,最好的方式是使用静态成员记录实例个数,并显式加入一些代码到 __init__()__del()__ 中去
class InstCt(object):
    count = 0 # count 是一个类属性
    def __init__(self):
        InstCt.count += 1 # 增加count
    def __del__(self):
        InstCt.count -= 1 # 减少count
    def howMany(self):
        return InstCt.count # 返回count

13.6 实例属性

13.6.1 “实例化”实例属性

设置实例的属性可以在实例创建后任意时间进行,也可以在能够访问实例的代码中进行。(体现Python动态语言特性)
  • 在构造器中首先设置实例属性 __init__
  • 默认参数提供默认的实例安装:默认参数应当是不变的对象
  • __init__() 应当返回None:不返回None会导致TypeError异常

13.6.2 查看实例属性

  • dir() 可以显示类属性,也可以打印所有实例属性
  • 与类相似,实例也有__dict__ 特殊属性(可以调用 vars(instance) 来获取)

13.6.3 特殊的实例属性

  • 实例仅有两个特殊属性:__class____dict__
  • __dict__ 仅有实例属性,没有类属性或特殊属性;而类的 __dict__ 仅有类属性和特殊属性
  • 建议不要修改 __dict__ 属性

13.6.4 内建类型属性

  • 内建类型的实例也可以通过 dir() 得到它的属性名字列表
  • 内建类型中没有 __dict__ 属性

13.6.5 实例属性 vs 类属性

  • 访问类属性:类属性(比如C.version)可以通过类或实例来访问(通过c.version访问类属性:Python会在实例中搜索名字,然后是类,再就是继承树中的基类),但只能通过类来设定或更新;尝试用实例来更新或设定类属性,会创建同名的实例属性,从而阻止对类属性的访问。
>>> c.version # 通过实例对类属性的访问
1.2
>>> c.version += 0.2 # 相当于实例c创建了version的实例属性,遮蔽了类属性的访问,不会更新类属性
>>> C.version += 0.1 # 类属性不变
>>> c.version # 实例再也不能访问version类属性了,因为已经存在同名的实例属性
1.4
  • 从实例中访问类属性需谨慎:同上;但在类属性是可变对象的情况下,也要注意:
>>> class Foo(object):
...     x = {2003: 'poe2'}
...
>>> foo = Foo()
>>> foo.x
{2003: 'poe2'}
>>> foo.x[2004] = 'valid path'
>>> Foo.x # 实例访问类属性又可以改变类属性了
{2003: 'poe2', 2004: 'valid path'}
  • 类属性持久性:静态成员,独立于实例;尽量使用类名修改类属性

13.7 绑定和方法调用

回顾:1. 方法是类内部定义的函数(类属性);2. 方法只有在其所属的类用于实例时,才能被调用。3. 任何一个方法定义中的第一个参数都是变量self,表示调用此方法的实例对象。

13.7.1 调用绑定方法

实例调用一个绑定方法时,self不需要明确地传入了。而没有实例时又需要调用一个非绑定方法时,必须传递self参数(比如是该方法属于类或子类的实例)

# C 和 T 两个类, C 有foo(self)方法
>>> c = C()
>>> c.foo() # 不需要传递self参数
>>> C.foo(c) # 非绑定方法需要传递self参数
>>> t = T()
>>> C.foo(t) # self不能是除C或C子类的实例外的实例
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unbound method foo() must be called with C instance as first argument (got T instance instead)

13.7.2 调用非绑定方法

  • 调用一个还没有任何实例的类中的方法的一个主要场景:派生一个子类,需要覆盖父类的方法,可能需要调用父类中想要覆盖的构造方法
class C(object):
    def __init__(self, id):
        self.id = id
class child(C):
    def __init__(self, id, age):
        C.__init__(self, id) # 调用父类的非绑定方法,此时未创造C实例
        self.age = age

13.8 静态方法和类方法

  • 静态方法:类中的函数,不需要实例,也不需要传入self的参数
  • 类方法:需要类而不是实例作为第一个参数,一般用 cls 参数

13.8.1 staticmethod()classmethod() 内建函数

  • 这两个内建函数用于将作为类定义的一部分的某一方法声明“标记”,“强制类型转换”或者“转换”为这两种类型的方法之一
# 静态方法
class TestStaticMethod(object):
    def foo():
        print 'calling static method foo()'
    foo = staticmethod(foo)

# 测试
tsm = TestStaticMethod()
TestStaticMethod.foo()
tsm.foo()

结果为:

calling static method foo()
calling static method foo()
# 类方法
class TestClassMethod(object):
    def foo(cls, id):
        print 'calling class method foo()'
        print id, ': foo() is part of class:', cls.__name__
    foo = classmethod(foo)

# 测试
tcm = TestClassMethod()
TestClassMethod.foo('001')
tcm.foo('001')

结果为:

calling class method foo()
001 : foo() is part of class: TestClassMethod
calling class method foo()
001 : foo() is part of class: TestClassMethod

13.8.2 使用函数修饰符

  • 它可以把一个函数应用到另个函数对象上,而且新函数对象依然绑定在原来的变量
# 静态方法
class TestStaticMethod(object):
    @staticmethod
    def foo():
        print 'calling static method foo()'

# 类方法
class TestClassMethod(object):
    @classmethod
    def foo(cls, id):
        print 'calling class method foo()'
        print id, ': foo() is part of class:', cls.__name__

13.9 组合

  • 类之间的关系:组合关系(has a),派生(继承)关系(is a)

13.10 子类和派生

  • 创建子类
class SubClassName(ParentClass1[, ParentClass2,...]):
    'optional class documentation string'
    class_suite

13.11 继承

  • 继承描述了基类的属性如何“遗传”给派生类。

13.11.1 __bases__类属性

13.11.2 通过继承覆盖方法

标记={P:父类,C:子类,p:父类实例,c:子类实例}

  • 重写父类方法
  • 子类实例调用被覆盖的父类方法:P.foo(c)
  • 可以直接写进子类的覆盖方法中:def foo(self): P.foo(self) ...
  • 更好的办法是使用 super() 内建方法
class C(P):
    def foo(self):
        super(C, self).foo()
        print 'Hi, I am C-foo()'
  • 重写__init__ 不会自动调用基类的 __init__
  • Python 使用基类名来调用类方法,而super()内建函数,使不需要明确提供父类名。这在改变继承关系时,只用改class语句那一行,而不用在大量代码中寻找被修改的那类的名字

13.11.3 从标准类型派生

  • 不可变类型:所有的 __new__ 方法都是类方法,需要显式地传入类作为第一个参数
# 派生基本类型(不可变类型)
class RoundFloat(float):
    def __new__(cls, val):
        return super(RoundFloat, cls).__new__(cls, round(val, 2))

# 测试
x = RoundFloat(1.5945)
print x
print type(x)
  • 可变类型
# 派生可变类型
# 可能不需要使用__new__方法
class SortedKeyDict(dict):
    def keys(self):
        return sorted(super(SortedKeyDict, self).keys())

# 测试
d = SortedKeyDict((('zheng-cai', 67), ('hui-jun', 68), ('xin-yi', 2)))
print 'By iterator:'.ljust(12), [key for key in d]
print 'By keys():'.ljust(12), d.keys()

结果为:

By iterator: ['zheng-cai', 'xin-yi', 'hui-jun']
By keys():   ['hui-jun', 'xin-yi', 'zheng-cai']

13.11.4 多重继承

使用多重继承,要考虑如何正确找到没有在当前(子)类定义的属性。方法解析顺序

  • 方式解释顺序(MRO)
    • 2.2 以前,算法:深度优先
    • 2.3 以后,算法:广度优先
  • 新式类也有一个__mro__ 属性,告诉我们查找顺序
  • 菱形效应引起MRO问题
    • 大部分类都是单继承的,多重继承只限用在对两个完全不相关的类进行联合,即mixin类
    • 版本2.2 中,类和类型的统一,一个简单的继承结构变成了一个菱形。

13.12 类、实例和其他对象的内建函数

13.12.1 issubclass()

  • 语法issubclass(sub, sup)
  • 允许不严格的子类(一个类视为自身的子类)
  • 第二个参数可以是可能的父类组成的元组:只要第一个参数是给定元组中任何一个候选子类时,就返回True

13.12.2 isinstance()

  • 语法:isinstance(obj1, obj2) 第二个参数可以接受元组
>>> isinstance(int, (type,))
True
>>> isinstance(1, (int, object))
True

13.12.3 hasattr(), getattr(), setattr(), delattr()

  • *attr()系列可以在各种对象中工作,不限于类和实例;第一个参数是对象,第二个参数是属性名
  • 在操作obj.attr时,就相当于调用*attr(obj, ‘attr’)系列函数
  • getattr()会在试图读取一个不存在的属性时,引发AttributeError异常,除非给出那个可选的默认参数 getattr(myInst, 'bar', 'oops')
  • setattr() 要么加入一个新的属性,要么取代一个已存在的属性

13.12.4 dir()

13.12.5 super()

  • 使用super()简化搜素一个合适祖先的任务,并且在调用它时,替你传入实例或类型对象
  • 对于每个定义的类,都有一个__mro__ 的属性,按照他们被搜索时的顺序,列出了备搜索的类
  • 语法:super(type[, obj])
    • 如果obj是一个实例,isinstance(obj, type) 就必须返回True
    • 如果obj是一个类或类型,issubclass(obj, type) 就必须返回True

13.12.6 vars()

没有提供参数,则将显示一个包含本地名字空间的属性,即locals()

13.13 用特殊方法定制类

  • 模拟标准类型
  • 重载操作符
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值