9. 类

9. 类

类提供了一种组合数据和功能的方法, 创建一个新类意味着创建一个新类型.python支持多继承, 派生类可以覆盖基类的方法, 可以调用基类的方法.

9.1 名称和对象

python的对象, 多个名称可以绑定到同一个对象, 即使它们不在同一个作用域. 在处理不可变类型时, 它们没有影响, 但是在处理可变类型时, 它改变将会导致都改变.
Test.py:

def Test(MyList):
    MyList[0] = "111"

List = ["1", "2", "3"]
test = Test(List)
print(List[0])

运行结果:

111

9.2 Python 作用域和命名空间

namespace 是一个从名字到对象的映射.
命名空间的例子:
1.存放内置函数的集合, 包含内置函数和内建的异常
2.模块中的全局名称
3.函数调用中的局部名称
不同的命名空间中的名称之间没有关系.


9.2.1 作用域和命名空间实例

nonlocal : 可以对父级作用域的变量进行修改,并且在当前作用域创建, 但是不能修改全局变量.
Test.py:

def scope_nonlocal():
    def test():
        nonlocal spam
        spam = "I Am nonlocal"   

    spam = "I Am local"
    test()
    print("after test:", spam)

spam = "I Am Global"
scope_nonlocal()
print("glocal:", spam)

运行结果:

after test: I Am nonlocal
glocal: I Am Global

global : 可以声明全局变量, 可在局部修改全局变量
Test.py:

def scope_global():
    global spam
    spam = "I Am New Global"

spam = "I Am Global"
scope_global()
print(spam)

运行结果:

I Am New Global

9.3 初探类

9.3.1 类定义语法

定义最简单的类:
Test.py:

class ClassName:
    <statement-1>
    .
    .
    .
    <statement-N>
9.3.2 类对象

类对象支持二种操作:属性引用和实例化.
属性引用指的是:根据类的实例获取其属性和类方法.
Test.py:

class MyClass:
    str = 123

    def ClassMothod(self):
        print("Mothod")

classTest = MyClass()
print(classTest.str)
classTest.ClassMothod()

运行结果:

123
Mothod

如何不创建实例也可以获取到其属性, 且可以动态的修改其属性, 一旦修改了将会导致之后的所有此类都会变化.
Test.py:

class MyClass:
    str = 123

    def ClassMothod(self):
        print("Mothod")

print(MyClass.str)

MyClass.str = 111
print(MyClass.str)

classTest = MyClass()
print(classTest.str)

运行结果:

123
111
111

同样可以可以获取类中的方法, 也可以动态修改其方法.
Test.py:

def NewClassMothod(self):
    return "newMothod"

class MyClass:
    str = 123

    def ClassMothod(self):
        print("Mothod")

MyClass.ClassMothod = NewClassMothod

ss = MyClass()
print(ss.ClassMothod())

运行结果:

newMothod

以上创建的实例都是不带参数的, 可以通过定义__init__()方法, 当类实例化操作时会自动为创建的类实例调用此方法.
Test.py:

class MyClass:
    def __init__(self, name):
        self.className = name 

classTest = MyClass("MyClass")
print(classTest.className)

运行结果:

MyClass
9.3.3 实例对象

实例对象的数据属性和方法, 实例里的方法和类中的方法不是一样的.
Test.py:

class MyClass:
    def myFunction(self):
        pass

print(type(MyClass.myFunction))
Test = MyClass()
print(type(Test.myFunction))

运行结果:

<class 'function'>
<class 'method'>
9.3.4 方法对象

我们可以看到类中的方法都是有一个参数self的, 但是我们调用的时候都没有传入此参数, 而且也没有报异常, 实际上此参数就是实例对象本身. 调用实例的方法其实就是类似于调用类的方法并且传入实例对象.
Test.py:

class MyClass:
    def myFunction(self):
        print("aa")
x = MyClass()
MyClass.myFunction(x)

运行结果:

aa

看看self 和 实例对象是否是一样的.
Test.py:

class MyClass:
    def myFunction(self):
        print(type(self))
x = MyClass()
x.myFunction()

运行结果:

<class '__main__.MyClass'>

附上官方运作原理:
当一个实例的非数据属性被引用时,将搜索实例所属的类。 如果名称表示一个属于函数对象的有效类属性,会通过合并打包(指向)实例对象和函数对象到一个抽象对象中的方式来创建一个方法对象:这个抽象对象就是方法对象。 当附带参数列表调用方法对象时,将基于实例对象和参数列表构建一个新的参数列表,并使用这个新参数列表调用相应的函数对象。

9.3.5 类和实例变量

实例变量是每个实例的唯一数据, 而类变量是类的所有实例共享的属性和方法.
Test.py:

class Myclass:
    classArg = "temp"
    def __init__(self, arg):
        self.arg = arg

Class1 = Myclass("class1")
Class2 = Myclass("class2")

print(Class1.classArg, Class2.arg)
print(Class1.arg, Class2.arg)

运行结果:

temp class2
class1 class2

当共享数据的类型是列表或是字典的时候, 当其发生变化也会影响到其他的实例.
Test.py:

class Myclass:
    classArg = "temp"
    classArgs = []
    def __init__(self, arg):
        self.arg = arg

Class1 = Myclass("class1")
Class2 = Myclass("class2")

# 初始状态
print(Class1.classArg, Class1.classArgs)
print(Class2.classArg, Class2.classArgs)

# 改变class1 的 值
Class1.classArg = "111"
Class1.classArgs.append("aaa")

# 输出现在的状态
print(Class1.classArg, Class1.classArgs)
print(Class2.classArg, Class2.classArgs)

运行结果:

temp []
temp []
111 ['aaa']
temp ['aaa']

可以使用self.方法名调用其他方法.
Test.py:

class Myclass:
    def myFunction1(self):
        print("我被调用了")
    def myFunction(self):
        self.myFunction()

test = Myclass()
test.myFunction1()

运行结果:

我被调用了

###9.4 继承
继承的语法.
Test.py:

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

BaseClassName必须定义于包含派生类定义的作用域内, 也可以定义在别的模块中, 使用BaseClassName.methodname(self, arguments)可以调用基类中的方法…
Main.py:

import Part

class Myclass(Part.base):
    def useBaseFunction(self):
        Part.base.function(self)
test = Myclass()
test.useBaseFunction()

Part.py:

class base:
    def function(self):
        print("baseFunction")

运行结果:

baseFunction

python中所有的方法都是virtual方法, 重载基类中的方法.
Main.py:

import Part

class Myclass(Part.base):
    def function(self):
        print("new function")
        Part.base.function(self)
test = Myclass()
test.function()

part.py:

class base:
    def function(self):
        print("baseFunction")

运行结果:

new function
baseFunction

使用isinstance(obj, type)检测obj实例对象是不是继承与type类型的, issubclass(type1, type2)可分别检测type1是否是type2的子类.
Main.py:

import Part

class Myclass(Part.base):
    def function(self):
        print("new function")
        Part.base.function(self)
testMyclass = Myclass()
testBaseClass = Part.base()
print(isinstance(testBaseClass, Myclass))
print(isinstance(testMyclass, Part.base))

print(issubclass(Part.base, Myclass))
print(issubclass(Myclass, Part.base))

运行结果:

False
True
False
True
9.4.1 多重继承

python可以使用多继承, 继承顺序采用的是C3线性化算法, 和广搜是不同的, 需要注意的是python新的类是都默认继承于object的, 可以通过mro()查看顺序.
Test.py:

class A1:
    pass
class A2:
    pass
class A3:
    pass
class B1(A1, A2):
    pass
class B2(A2):
    pass
class B3(A2):
    pass
class C(B1, B2, B3):
    pass


print(C.mro())

# c b1 a1 B2 B3 A2 

运行结果:

[<class '__main__.C'>, <class '__main__.B1'>, <class '__main__.A1'>, <class '__main__.B2'>, <class '__main__.B3'>, <class '__main__.A2'>, <class 'object'>]

C3 线性算法的公式:
L[object] = [object] L[C(B1…BN)] = [C] + merge(L[B1]…L[BN], [B1, … ,BN])

按如上的继承关系代入.
首先先计算 L[B1(A1,A2)]]
= [B1] + merge(L[A1],L[A2],[A1,A2])
= [B1] + merge([A1,obj],[A2,obj],[A1,A2])
= [B1,A1] + merge([obj],[A2,obj],[A2])
= [B1,A1,A2] + merge([obj],[obj])
= [B1,A1,A2,obj]

再计算L[B2[A2]]:
= [B2] + merge(L[A2])
= [B2] + merge([A2,obj])
= [B2,A2,obj]

再计算 L[B3[A2]]:
= [B3] + merge(L[A2])
= [B3] + merge([A2,obj])
= [B3,A2,obj]

最后计算,并代入
L[C(B1,B2,B3)]
= [C] + merge([B1,A1,A2,obj],[B2,A2,obj],[B3,A2,obj],[B1,B2,B3])
= [C,B1] + merge([A1,A2,obj],[B2,A2,obj],[B3,A2,obj],[B2,B3])
= [C,B1,A1] + merge([A2,obj],[B2,A2,obj],[B3,A2,obj],[B2,B3])
= [C,B1,A1,B2] + merge([A2,obj],[A2,obj],[B3,A2,obj],[B3])
= [C,B1,A1,B2,B3] + merge([A2,obj],[A2,obj],[A2,obj])
= [C,B1,A1,B2,B3,A2] + merge([obj],[obj],[obj])
= [C,B1,A1,B2,B3,A2,obj]

以上原理可以总结为, 展开后, 先找到头部, 然后去找别的列表中出了头部以外的地方有没有此参数, 如果有的话,这个头部不输出, 跳到有的那个列表, 将其头部作为新的开始继续执行此操作, 如果没有的话直接输出此参数, 并且拿起头部的下一个参数的作为头部继续执行.

9.5 私有变量

python 没有那种只限在对象内部访问的的变量.
xx : 共有变量
_xx : 潜质下划线, 私有化属性和方法, 类对象和子类可以访问, from somemodule import *禁止导入
__xx : 双前置下划线, 私有化属性和方法, 无法在外部直接使用此名字进行访问, 会被改为_classname__xx.
__xx__ : 双前后下划线, 系统定义名字.
xx_ : 单后置下划线, 用于避免与Python关键字的冲突.

Test.py:

class Test:
    def __init__(self):
        self.name = "1"
        self._name = "2"
        self.__name = "3"
        self.name_ = "4"
ss = Test()
print(ss.name, ss._name, ss._Test__name, ss.name_)

运行结果:

1 2 3 4

9.6 迭代器

我们在使用很多容器对象的时候大都可以使用for进行循环.
Test.py:

for i in [1, 2, 3, 4]:
    print(i)

for i in (1, 2, 3, 4):
    print(i)

for i in {"one":1, "two":2}:
    print(i)
    
for i in "1234":
    print(i)

运行结果:

1
2
3
4
1
2
3
4
one
two
1
2
3
4

其实在for循环的内部是调用了容器的iter(), 其将返回一个定义了__next__()方法的迭代器对象, 当元素访问完了之后会引发一个异常StopIteration来停止循环, 可以使用next()方法来调用__next__(). iter()将会调用内部的__iter__()方法.
Test.py:

str = "1234"
iterTest = iter(str)

while(True):
    try:
        print(next(iterTest))
    except StopIteration as e:
        print("Done")
        break

运行结果:

1
2
3
4
Done

现在使用我们自己的类模拟一个符合迭代器幕后机制的类.
Test.py:

class iterClass:
    def __init__(self, data):
        self.data = data
        self.index = -1
    def __iter__(self):
        return self
    def __next__(self):
        if self.index + 1 < len(data) > 0:
            self.index = self.index + 1
            return self.data[self.index]
        else:
            raise StopIteration

data = [1, 2, 3, 4]

Test = iterClass(data)
for i in Test:
    print(i)

运行结果:

1
2
3
4

9.7 生成器

使用yield代替return返回可以再下次返回时继续执行上次的过程, 存有上次的所有状态.
Test.py:

def returnFunction(data):
    for i in data:
        return i

def yieldFunction(data):
    for i in data:
        yield i

for i in returnFunction("1234"):
    print(i)
print("******")
for i in yieldFunction("1234"):
    print(i)

运行结果:

1
******
1
2
3
4

生成器会自动创建__iter__() 和 next()方法, 还会自动引发stopiteration.

9.8 生成器表达式

可以使用类似于列表推导式的方式编写生成器表达式, 列表推导式是用[]包裹起来的, 生成器表达式采用(),它们的场景不同使用之处在于, 列表表达式一般用于返回一个列表对象用于赋值, 生成器表达式一般用于返回给外层函数使用, 其内部有__iter__()和__next__() 方法在里面, 其比列表表达式更节省内存.
Test.py:

ans = list(i*i for i in range(10))
print(ans)

print(type((i for i in range(10))))
print(type([i for i in range(10)]))

运行结果:

[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
<class 'generator'>
<class 'list'>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值