--类

Posted TonyW92

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了--类相关的知识,希望对你有一定的参考价值。

导读



类对于Java同学来说无比熟悉,每个class都是一个类

类包括2个部分:属性和方法
属性是用来描述相同对象的静态特征
方法是用来描述相同对象的动态特征

Python中的类


在python中定义一个class

class Person:
    //构造函数
    def __init__(self):
        pass
    def getClassDesc(self):
        return "Person Class"


self
类方法第一个参数是self,不能省略。用于接收实例化过程中传入的所有参数,但是在实例化的过程中,这个self不需要外部传入。这个对于写Java的同学来说可能比较别扭。
在python中,外部传入的数据都可以赋给self,而不需要像Java一样创建很多成员变量,而self本身就是一个实例对象。python作为一门动态的脚本语言和Java有一个本质的区别就是,当Java一个类编写完之后通过编译得到class文件就再也不能修改了,而python随时可以修改类或者实例对象,比如增加一个变量或者删除一个变量。所以我们可以为self不断增加变量

def__init__
等价于Java的构造函数

生成实例对象
p = Person()
print(type(p))
print(p.getClassDesc())


类属性和实例属性
直接通过类调用的变量称为类属性,静态数据
通过将类实例化后,用实例得到的属性叫做实例属性

通过实际的例子可以更好的理解

class A:
    v = 8    

print(A.v)
a = A()
print(a.v)

a.v就是实例属性 A.v就是类属性
可以得到结果
8
8

然后我们对a.v进行修改
a = A()
a.v = 9
print(a.v)
print(A.v)

可以得到结果
9
8


可以得出一个结果实例属性发生变化时不会修改类属性
这是为什么呢?
原因就是a.v = 9这个操作其实是实例a生成了一个新的属性v然后覆盖了之前的v
不信可以用del a.v试试

a = A()
a.v = 9
print(a.v)
del a.v
print(a.v)

可以得到结果
9
8

反过来如果我们修改类属性呢
a = A()
A.v = 10
print(a.v)
print(A.v)

可以得到结果
10
10


可以初步得出一个结论:如果类中变量是一个不可变对象时(之前在1章中讲过什么是可变对象),修改实例属性不能影响类属性,但是修改类属性会影响实例属性

那紧接着我们看看当类中变量是可变对象时,会有什么结果

class A:
   list = [1,2,3,4]

a = A()
A.list.appen(5)
print(A.list)
print(a.list)    
a .list.remove(4)

可以得到结果
[1,2,3,4,5]
[1,2,3,4,5]

[1,2,4,5]
[1,2,4,5]


可以得到一个结论,如果类中变量是可变对象,类属性和实例属性可以相互影响

继承
正对于java同学来说肯定不陌生
class Person(object):
pass

多重继承
区别于Java中的单继承,python支持多继承

class Boy(A,B):
    pass


继承的类和属性广度优先
怎么理解呢

来看个例子
#! /usr/bin/env python
#coding=utf-8

class A:
   def func(self):
       print("A func")

class B:
    def func(self):
        print("B func")

    def getClassName(self):
        print("class name : B")

class C(A,B):
    def getClassName(self):
        print("class name : C")

class D(C):
    pass

if __name__ == "__main__":
    d = D()
    d.func()
    d.getClassName()

结果
A func
class name : C

可以看到遍历路径是 D->C->A->B
符合之前说的广度优先

Super

这里和Java里的super在表象上看一致,其实没有半毛钱关系
python中的super工作原理是

def super(cls, inst):
    mro = inst.__class__.mro()
    return mro[mro.index(cls) + 1]


其中,cls 代表类,inst 代表实例,上面的代码做了两件事:
● 获取 inst 的 MRO 列表
● 查找 cls 在当前 MRO 列表中的 index, 并返回它的下一个类,即 mro[index + 1]
当你使用 super(cls, inst) 时,Python 会在 inst 的 MRO 列表上搜索 cls 的下一个类。

举个例子

class Base(object):
    def __init__(self):
        print "enter Base"
        print "leave Base"

class A(Base):
    def __init__(self):
        print "enter A"
        super(A, self).__init__()
        print "leave A"

class B(Base):
    def __init__(self):
        print "enter B"
        super(B, self).__init__()
        print "leave B"

class C(A, B):
    def __init__(self):
        print "enter C"
        super(C, self).__init__()
        print "leave C"

>>> c = C()
enter C
enter A
enter B
enter Base
leave Base
leave B
leave A
leave C


看下C的方法解析顺序

>>> C.mro()   # or C.__mro__ or C().__class__.mro()

[__main__.C, __main__.A, __main__.B, __main__.Base, object]


当调用super(C, self).init()会执行A中的init(self)方法然后调用A中的super(A, self).init()又会根据表中顺序找到B依次下去

和Java中直接调用父类方法完全不一样

静态方法
@staticmethod

类方法
@classmethod

实例方法只能被实例对象调用,静态方法(由@staticmethod装饰的方法)、类方法(由@classmethod装饰的方法),可以被类或类的实例对象调用。
实例方法,第一个参数必须要默认传实例对象,一般习惯用self。
静态方法,参数没有要求。
类方法,第一个参数必须要默认传类,一般习惯用cls。

看个例子
class Foo(object):
    X = 1
    Y = 2

    @staticmethod
    def averag(*mixes):
        return sum(mixes) / len(mixes)

    @staticmethod
    def static_method():
        return Foo.averag(Foo.X, Foo.Y)

    @classmethod
    def class_method(cls):
        return cls.averag(cls.X, cls.Y)


class Son(Foo):
    X = 3
    Y = 5

    @staticmethod
    def averag(*mixes):
        return sum(mixes) / 3

p = Son()
print(p.static_method())
print(p.class_method())

结果:
1.5
2.6666666666666665


如果子类继承父类的方法,子类覆盖了父类的静态方法,
子类的实例继承了父类的static_method静态方法,调用该方法,还是调用的父类的方法和类属性。
子类的实例继承了父类的class_method类方法,调用该方法,调用的是子类的方法和子类的类属性。

私有化

准备私有化的属性前面加上__

@property
可以调用私有化属性

特殊属性

dict
slots 申明类属性,只要初始化了类属性,实例就不能修改这个属性了
setattr(self, name, value) 如果给name赋值,就调用这个方法
getattr(self, name) 如果name被访问,同时它不存在,就调用这个方法
getattribute(self, name) 当name被访问,无论存不存在都调用
delattr(self, name)如果要删除name,该方法被调用
name

python中if name == ‘main
在cmd 中直接运行.py文件,则name的值是’main‘;
而在import 一个.py文件后,name的值就不是’main‘了;
从而用if name == ‘main‘来判断是否是在直接运行该.py文件

获得实例属性
先从dict中找,没有就从类属性中找

生成器

在需要生成大量数据时,一次性全部读到内存中一定不是一个好办法,我们用到多少数据就读取多少数据,可以减轻内存开销,提升程序性能,这就需要一边循环一边计算。在Python中,这种一边循环一边计算的机制,称为生成器:generator。

要创建一个generator,有很多种方法。第一种方法很简单,还记得之前讲到的列表解析器吗?只要把一个列表生成式的[]改成(),就创建了一个generator:

>>> l = [x for x in range(10)]
>>> l
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> g = (x for x in range(10))
>>> g
<generator object <genexpr> at 0x1022ef630>


生成器内部使用的是迭代器,每用next(g)取一个数据,游标下移一位,直到取完抛出StopIteration错误.如果用for循环取则没有这个顾虑

>>> g = (x * x for x in range(10))
>>> for n in g:
...     print(n)
... 
0
1
2
3
4
5
6
7
8
9


和列表不同的是,再循环取一次生成器,就得到空,因为游标已经在最后位置了

>>> for n in g:
...     print(n)
... 
>>


yield

yield内部实现支持了迭代器协议,同时yield内部是一个状态机,维护着挂起和继续的状态
这是什么意思呢?我们先来看个例子

def foo():
    print('step 1')
    yield 1
    print('step 2')
    yield(3)
    print('step 3')
    yield(5)

f = foo()
for i in f:
print(i)

得到的结果就是
step 1
1
step 2
2
step 3
3


在foo这个函数中加入yield这个关键字,这样foo()函数就变成了一个生成器,它会返回一个生成器类型的对象
再来看看刚刚的概念,维护着挂起和继续的状态程序每执行到yiled这里就会产生一个类似return的效果,中止函数继续执行,但是和return不同的是,yeild只是把函数挂起了,下次执行next()的时候,就会从当前yiled后面继续执行

其实,生成器函数返回生成器的迭代器。 “生成器的迭代器”这个术语通常被称作”生成器”。这也是另一种创建一个generator的方法。

总结



学完这章,我们就具备了编写python程序的有绝大数知识,已经可以编写许多实用的小工具了,在实践中不断提升编程能力,扩充python知识。推荐大家上github上看看一些大牛的开源框架,看看他们是如何利用这些基础的语法知识,写出精妙的框架。在下一章我们可以一起来看下python的I/O处理

以上是关于--类的主要内容,如果未能解决你的问题,请参考以下文章

Java中啥是适配器模式及其作用是啥??

J2EE常见的五种模式是啥

[西方哲学史 导图001]作为意志与表象的世界 叔本华 读书导图

编程创意秀|本期分享者傅昱铼《奇幻动画-闹别扭的飞机》

别扭的java ROOT 目录

BFC的表象认识