python学习总结9 - 类

python学习总结9 - 类

类把数据、功能绑定在一起,创建新类就是创建新的对象 类型,从而创建该类型的新 实例 。类实例支持维持自身状态的属性,还支持(由类定义的)修改自身状态的方法。

9.1 Python 作用域和命名空间

  • 点号之后的名称是 属性,例如:表达式 z.real 中,real 是对象 z 的属性
  • 对模块中名称的引用是属性引用
  • 模块属性和模块中定义的全局名称之间存在直接的映射:它们共享相同的命名空间!
  • nonlocal 赋值会改变 scope_test 对 spam 的绑定
  • global 赋值会改变模块层级的绑定。
def scope_test():
    def do_local():
        spam = 'local spam'
    # 碰到nonlocal,调用本函数,以下面的变量值为主
    def do_nolocal():
        nonlocal spam
        spam = 'nolocal spam'
    # global同上,另外global对应的变量值是整个py文件的
    def do_global():
        global spam
        spam = 'global spam'

    spam = 'test spam'
    do_local()
    print("After local assignment:", spam)
    do_nolocal()
    print("After nonlocal assignment:", spam)
    do_global()
    print("After global assignment:", spam)

scope_test()
print("In global scope:", spam)
C:\Python38\python.exe C:/Users/X21201/Desktop/python/tiaoce.py
After local assignment: test spam
After nonlocal assignment: nolocal spam
After global assignment: nolocal spam
In global scope: global spam

9.3 初探类

9.3.1. 类定义语法

class ClassName:
    <statement-1>
    .
    .
    .
    <statement-N>

9.3.2. Class 对象

类对象支持2种操作:属性引用、实例化

class MyClass:
    """A simple example class"""
    i = 12345

    def f(self):
        return 'hello world'
  1. 属性引用
  • MyClass.iMyClass.f 就是类的属性引用,
  1. 实例化
  • 创建类的新 实例 并将此对象分配给局部变量 x = MyClass()
  • 许多类喜欢创建 带有特定初始状态的自定义实例,为此类定义可能包含 __init__(),当一个类定义了 __init__() 方法时,
    类的实例化操作会自动为新创建的类实例发起调用 init()

9.3.3. 实例对象

上面把类实例化了,并把实例化的类给了变量X,x = MyClass()现在我们能用实例对象做什么? 实例对象的唯一操作:属性引用,有两种有效的属性名称:数据属性和方法。

9.3.4. 方法对象

  • 调用 x.f() 时并没有带参数,方法的特殊之处就在于实例对象会作为函数的第一个参数被传入
  • 调用 x.f() 其实就相当于 MyClass.f(x)
class MyClass:
    """A simple example class"""
    i = 12345

    def f(self):
        return 'hello world'


x = MyClass()
x.f()
MyClass.f(x)

9.3.5. 类和实例变量

  • 一般来说,实例变量用于每个实例的唯一数据,正确的类设计应该使用实例变量,如:
class Dog:

    # tricks = []             # 错误的类变量使用

    def __init__(self, name):
        self.name = name
        self.tricks = []  # 正确的类变量使用

    def add_trick(self, trick):
        self.tricks.append(trick)


d = Dog('旺财')
d.add_trick('小跑')
print(d.tricks)  # ['小跑']
e = Dog('泰迪')
e.add_trick('冲刺')
print(d.tricks)  # ['小跑']
  • 错误的类变更使用,类变量用于类的所有实例共享的属性和方法,如下类变量中的tricks被所有实例调用,如下:
class Dog:

    tricks = []             # mistaken use of a class variable

    def __init__(self, name):
        self.name = name

    def add_trick(self, trick):
        self.tricks.append(trick)


d = Dog('旺财')
d.add_trick('小跑')
print(d.tricks)  # ['小跑']
e = Dog('泰迪')
e.add_trick('冲刺')
print(d.tricks)  # ['小跑', '冲刺']

9.4. 补充说明

如果同样的属性名称同时出现在实例和类中,则属性查找会优先选择实例,如下:

class Warehouse:
    purpose = 'storage'
    region = 'west'


w1 = Warehouse()
print(w1.purpose, w1.region)

w2 = Warehouse()
w2.region = '改值了'
print(w2.region, w2.purpose)
C:\Python38\python.exe C:/Users/X21201/Desktop/python/tiaoce.py
storage west
改值了 storage

方法可以通过使用 self 参数的方法属性调用其他方法,例如:

class Bag:
    def __init__(self):
        self.data = []

    def add(self, x):
        self.data.append(x)

    def addtwice(self, x):
        self.add(x)
        self.add(x)

任何一个作为类属性的函数都为该类的实例定义了一个相应方法,函数定义的文本并非必须包含于类定义之内:将一个函数对象赋值给一个局部变量也是可以的,例如:

# 函数f1被定义到了C类外面
def f1(self, x, y):
    return min(x, x+y)

class C:
    f = f1

    def g(self):
        return 'hello world'

    h = g

9.5. 继承

利用继承机制,新的类可以从已有的类中派生。那些用于派生的类称为这些特别派生出的类的“基类”。如果不支持继承,语言特性就不值得称为“类”。

  • 派生类定义的语法:
class DerivedClassName(BaseClassName):
    <statement-1>
    .
    .
    .
    <statement-N>
  • 允许用其他任意表达式代替基类名称所在的位置,当基类定义在另一个模块中的时候:
class DerivedClassName(modname.BaseClassName):
  • 派生类定义的执行过程与基类相同,如果请求的属性在类中找不到,搜索将转往基类中进行查找。 如果基类本身也派生自其他某个类,则此规则将被递归地应用。
  • 派生类可能会重写其基类的方法,因为方法在调用同一对象的其他方法时没有特殊权限,基类方法最终可能会调用覆盖它的派生类的方法,
    有一种方式可以简单地直接调用基类方法:即调用 BaseClassName.methodname(self, arguments)
  • Python有两个内置函数可被用于继承机制: isinstance() \ issubclass()

9.5.1. 多重继承

Python 也支持一种多重继承。 最简单的情况下,你可以认为搜索从父类所继承属性的操作是深度优先、从左至右的,当层次结构中存在重叠时不会在同一个类中搜索两次。,
带有多个基类的类定义语句如下所示:

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

要了解更多细节,请参阅 https://www.python.org/download/releases/2.3/mro/

9.6. 私有变量

大多数 Python 代码都遵循这样一个约定:

  • _spam:无论它是函数、方法、数据成员(类属性),带有一个下划线的名称 (例如 _spam) 应该被当作是 API 的非公有部分,
  • __spam:由于存在对于类私有成员的有效使用场景(例如避免名称与子类所定义的名称相冲突),因此存在对此种机制的有限支持,称为 名称改写任何形式为 __spam 的标识符(至少带有两个前缀下划线,至多一个后缀下划线)的文本将被替换为 _classname__spam,其中 classname 为去除了前缀下划线的当前类名称。 这种改写不考虑标识符的句法位置,只要它出现在类定义内部就会进行。改写规则的设计主要是为了避免意外冲突;访问或修改被视为私有的变量仍然是可能的
class Mapping:
    def __init__(self, iterable):
        self.items_list = []
        # self.__update(iterable)
        self.__update = 'tengfei'

    def update(self, iterable):
        for item in iterable:
            self.items_list.append(item)

    # __update = update   # private copy of original update() method

class MappingSubclass(Mapping):

    def __init__(self, iterable):
        super().__init__(iterable)
        self.__update = 'jiao'
        print(self.__update)

    def update(self, keys, values):
        # provides new signature for update()
        # but does not break __init__()
        for item in zip(keys, values):
            self.items_list.append(item)
            print(self.items_list)

x = MappingSubclass('腾哥儿')
print(x)

当注释掉self.__update后运行报错:AttributeError: 'MappingSubclass' object has no attribute '_MappingSubclass__update',上面的示例即使在 MappingSubclass 引入了一个 __update 标识符的情况下也不会出错,因为它会在 Mapping 类中被替换为 _Mapping__update 而在 MappingSubclass 类中被替换为 _MappingSubclass__update

9.7. 杂项说明

将一些命名数据项捆绑在一起。 这种情况适合定义一个空类:

class Employee:
    pass

john = Employee()

john.name = 'John Doe'
john.dept = 'computer lab'
john.salary = 1000
john.jiao = 'jiao'

print(john.name)

一段需要特定抽象数据类型的 Python 代码往往可以被传入一个模拟了该数据类型的方法的类作为替代。 例如,如果你有一个基于文件对象来格式化某些数据的函数,你可以定义一个带有 read() 和 readline() 方法从字符串缓存获取数据的类,并将其作为参数传入。

9.8 迭代器

for element in [1,2,3]:
    print(element)
for element in (1,2,3):
    print(element)
for key in {'one':1, 'two':2}:
    print(key)
for char in '123':
    print(char)
for line in open('myfile.txt'):
    print(line, end='')

for 语句会在容器对象上调用 iter(),该函数返回一个定义了 __next__() 方法的迭代器对象,此方法逐一访问容器中的元素,当元素用尽时,__next__() 将引发 StopIteration 异常来通知终止 for 循环,如下运作机制:

it = iter('adc')
print(it)
print(next(it))
print(next(it))
print(next(it))
print(next(it))

给类添加迭代器,定义一个 __iter__() 方法来返回一个带有 __next__() 方法的对象。 如果类已定义了 __next__(),则 __iter__() 可以简单地返回 self

class Reverse:
    """用于向后循环序列的迭代器。"""
    def __init__(self, data):
        self.data = data
        self.index = len(data)

    def __iter__(self):
        return self

    def __next__(self):
        if self.index == 0:
            raise StopIteration
        self.index = self.index - 1
        return self.data[self.index]

rev = Reverse('spam')
iter(rev)
for char in rev:
    print(char)

9.9. 生成器

generator – 生成器

  • 返回一个 generator iterator 的函数。它看起来很像普通函数,不同点在于其包含 yield 表达式以便产生一系列值供给 for-循环使用或是通过 next() 函数逐一获取。
  • 通常是指生成器函数,但在某些情况下也可能是指 生成器迭代器。如果需要清楚表达具体含义,请使用全称以避免歧义。
  • 生成器 是一个用于创建迭代器的简单而强大的工具。它们的写法类似于标准的函数,但当它们要返回数据时会使用 yield 语句。 每次在生成器上调用 next() 时,它会从上次离开的位置恢复执行(它会记住上次执行语句时的所有数据值)。
def reverse(data):
    for index in range(len(data)-1, -1, -1):
        yield data[index]

for char in reverse('golf'):
    print(char)
  • 可以用生成器来完成的操作同样可以用前一节所描述的基于类的迭代器来完成。 但生成器的写法更为紧凑,因为它会自动创建 __iter__()__next__() 方法

9.10 生成器表达式

某些简单的生成器可以写成简洁的表达式代码,类似于列表推导式,但外层为圆括号而非方括号。 这种表达式被设计用于生成器将立即被外层函数所使用的情况

a = sum(i*i for i in range(10))
print(a)

xvec = [10,20,30]
yvec = [7,5,3]
b = sum(x*y for x,y in zip(xvec,yvec))
print(b)

# unique_words = set(word for line in page  for word in line.split())
# valedictorian = max((student.gpa, student.name) for student in graduates)

data = 'golf'
c = list(data[i] for i in range(len(data)-1, -1, -1))
print(c)
C:\Python39\python.exe C:/Users/mc/Desktop/python基础/tiaoce.py
285
260
['f', 'l', 'o', 'g']
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

阿_焦

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值