9. 类

本文深入探讨Python类的概念,包括self的作用、属性与方法的区别、继承机制、异常处理、迭代器及生成器等内容,并通过实例帮助理解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

python 类中的 self 即指代  当前类的命名空间, 私以为和 C++ 中的 this 关键字同义.
python 类 中的数据属性 和 局部变量一样, 无需声明, 第一次使用即可生成.

sample:
class M:
    str = 'hello'
    def __init__(self):
        str = 'zhangpzh5'

x = M()
x.count = 5
print x.count

当然咯, 初始化类的对象, 如果要使之具有初始状态, 则可以在 __init__ 函数的参数列表中扩展参数列表.


sample:
class M:

    str = 'hello'
    count = 5

def __init__ (self , cnt):
    self.count = cnt

def f(self):
    return 'hello world'


x = M(10)
print x.count

输出5


注意到,实例对象x作为一个参数传给了函数. x= M(10)相当于 M.__init__(x,10)

x.f()相当于 M.f(x)

这就是所谓的 方法对象


数据属性会覆盖同名的方法属性, 为了避免意外的名称冲突,这在大型程序中是极难发现的 Bug,使用一些约定来减少冲突的机会是明智的。

函数定义代码不一定非得定义在类中:也可以将一个函数对象赋值给类中的一个局部变量。 例如:


# Function defined outside the class
def f1(self, x, y):
    return min(x, x+y)

class C:
    f = f1
    def g(self):
        return 'hello world'
    h = g

每个值都是一个对象,因此每个值都有一个 类( class ) (也称为它的 类型( type ) ),它存储为 object.__class__

继承:

class DerivedClassName(BaseClassName):
    <statement-1>
    .
    .
    .
    <statement-N>

若基类定义在另一个模块中, 则使用表达式继承
class DerivedClassName(modname.BaseClassName):

对于一个子类, 如果在类中找不到对应的属性, 就搜索基类, 在找到对应属性之前, 这个集合会按 基类链 递归上去.

isinstance() 用于检查实例类型: isinstance(obj, int) 只有在 obj.__class__int 或其它从 int 继承的类型

issubclass() 用于检查类继承:    issubclass(bool, int)True ,因为 bool 是 int 的子类。但是, issubclass(unicode,str)False ,因为 unicode 不是 str 的子类(它们只是共享一个通用祖先类 basestring ).

在继承的时候,要注意命名空间的问题:
如下例:  knife 也是student类对象s的一个属性, 但是age不是. 因为在people类中, age定义在方法中.


class people:
    knife = 5 
    def __init__(self):
        age = 20.0
        heigh = 175
    def f(self):
        return 'hello people'
    def great(self):
        return 'I love this world !'

class student(people):
    def __init__(self):
        ID = 12348157
    def f(self):
        return 'hello student'


s = student()
print s.knife
print s.great();
# print s.age

多继承:
class DerivedClassName(Base1, Base2, Base3):
    <statement-1>
    .
    .
    .
    <statement-N>

  若派生类 中找不到某属性, 那么在自然情况下:  python会先从Base1 开始 dfs搜索它 的基类链, 若找不到, 则从Base2 开始dfs搜索它的基类链.

实际上, super()可以动态改变解析顺序


私有变量:

        只能从对像内部访问的“私有”实例变量,在 Python 中不存在。然而,也有一个变通的访问用于大多数 Python 代码:以一个下划线开头的命名(例如 _spam )会被处理为 API 的非公开部分(无论它是一个函数、方法或数据成员)。它会被视为一个实现细节,无需公开。
        因为有一个正当的类私有成员用途(即 避免子类里定义的命名与之冲突),Python 提供了对这种结构的有限支持,称为 name mangling (命名编码) 。任何形如 __spam 的标识(前面至少两个下划线,后面至多一个),被替代为 _classname__spam ,去掉前导下划线的 classname 即当前的类名。此语法不关注标识的位置,只要求在类定义内。

举例说明:

class father:
    def __init__(self,iterable):
        self.myList = []
        self.__update(iterable)

    def update(self , iterable):
            self.myList.append("This is father's update method !")
    
    __update = update

class son(father):
    def update(self, iterable):
        self.myList.append("This is son's update method !")

list = []
s = son(list)

for string in s.myList:
    print string,

如上图所示, son类的对象s在初始化的时候, 调用 父类father的__init__方法,  方法最后调用了__update, 由于__update只在类father中有效,  python会在father类中寻找__update的定义 , 发现 __update = update, 因此 python断定, 应当调用当前类中的update函数, 因此 程序的输出 为 "This is father' s update method !"
      但是,若将__init__方法中的最后一行改为:  "self.update(iterable)" , 则son类中的update方法会覆盖父类中的update方法, 程序的输出为 "This is son's update method !"
    
      说到这里,突然想起override和overload

① override(重写)指的是子类的方法覆盖父类的方法,且方法名相同, 参数列表相同
② overload(重载)指的是两个方法的名字相同,但是参数列表不相同.   两方法可以在一个类中,也可以不在一个类中.


Pascal中的record 和 C中的struct 都可以将特定数据类型 捆绑在一起 , 构成一个新的数据结构:

python中直接使用类可以达到此目的:

代码如下:

class Employment:
pass


John = Employment()
John.name = 'John.J.Rambo'
John.words = 'You won\'t beat me !'

异常也是类:

两种异常抛出形式:

① raise Class
② raise Instance

解释:
① raise Class 是 raise Class()的简写
② Instance 必须是Class或其派生类的一个实例

发生的异常类型如果是 except子句中列出的类或其派生类, 那么它们就是相符的
但是如果发生的异常类型是except子句中列出的类的基类,那么它们就是不相符的

一个例子:

class A(Exception):
    pass

class B(A):
    pass

class C(B):
    pass

list = [C,B,A]

for l in list:
    try:
        raise l()
    except C:
        print 'C'
    except B:
        print 'B'
    except A:
        print 'A'

屏幕将输出 C B A

迭代器:
大多数容器对象都可以使用for遍历
在后台, for语句在容器对象中调用iter(). 该函数返回一个定义了 next() 方法的迭代器对象,它在容器中逐一访问元素。
没有后续的元素时, next() 抛出一个 StopIteration 异常通知 for 语句循环结束.

在了解了迭代器协议的后台机制后, 定义一个 __iter__ 方法, 定义一个 __iter__() 方法,使其返回一个带有 next() 方法的对象。如果这个类已经定义了 next() ,那么 __iter__() 只需要返回 self:

一个例子:

class Reverse:
    def __init__(self,Str):
        self.Str = Str
        self.index = len(Str)
    def __iter__(self):
        return self
    
    def __next__(self):
        if(self.index == 0):
            raise StopIteration
        self.index -= 1
        return self.Str[self.index]
    next = __next__

rev = Reverse("zhangpzh")

for char in rev:
    print char,


生成器:

生成器(generator) 是一种可以很简单地实现迭代器的方式, 比写一个类(基于类的迭代器)更方便些.  需要返回数据时使用yield语句. 生成器会自动创建 __iter__方法 和 next 方法. 关键是, 在两次执行之间, 局部变量和执行状态都会被保存下来. 终结时, 它也会抛出 StopIteration 异常

一个例子:

def forward(data):
    for index in range(0,len(data),1):
        yield data[index]

for char in forward('Coke Cola'):
    print char,


此例将 正向输出  Coke Cola

生成器表达式:

这将在我的博文  "推导式与生成器表达式" 中和推导式一起介绍.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值