Python基础IV---类

这篇博客主要是阅读python之旅 时做的笔记。提取出最主要的知识点,供个人在以后中快速查阅。

类基本定义方式

  • 属性或方法名称前面加上两个下划线,则外部无法访问。
  • 如果是__xx__形式,则变量是特殊变量,就能拿直接访问。。
  • 所有的类最终继承自object类。
class Animal(object):
    def __init__(self, name):
        self.name = name
    def greet(self):
        print 'Hello, I am %s.' % self.name
>>> dog1 = Animal('dog1')
>>> dog1.name
'dog1'

# 如果把name改成__name,则无法访问该属性。
class Animal(object):
    def __init__(self, name):
        self.__name__ = name
    def greet(self):
        print 'Hello, I am %s.' % self.name
>>> dog1 = Animal('dog1')
>>> dog1.name
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'Animal' object has no attribute '__name'

获取对象信息

  • type(obj)
  • isinstance(obj, type)
  • hasattr(obj, attr)/getattr/setattr
  • dir(obj)

继承和多态

class Animal(object):
    def __init__(self, name):
        self.name = name
    def greet(self):
        print 'Hello, I am %s.' % self.name

class Dog(Animal):
    def greet(self): #对父类的方法进行重写
        print 'WangWang.., I am %s. ' % self.name
    def run(self):   #添加新的方法
        print 'I am running.I am running'

>>> dog = Dog('dog')
>>> dog.greet()
WangWang.., I am dog.
>>> dog.run()
I am running

多态:对不同类型的变量进行相同的操作,它会根据对象(或类)类型的不同而表现出不同的行为。
直接看例子吧,因为太简单了。

class Animal(object):
    def __init__(self, name):
        self.name = name
    def greet(self):
        print 'Hello, I am %s.' % self.name

class Dog(Animal):
    def greet(self):
        print 'WangWang.., I am %s.' % self.name

class Cat(Animal):
    def greet(self):
        print 'MiaoMiao.., I am %s' % self.name

def hello(animal):
    animal.greet()

>>> dog = Dog('dog')
>>> hello(dog)
WangWang.., I am dog.
>>>
>>> cat = Cat('cat')
>>> hello(cat)
MiaoMiao.., I am cat

类方法和静态方法

  • 类方法使用 @classmethod 装饰器,可以使用类(也可使用实例)来调用方法。
  • 静态方法使用 @staticmethod 装饰器,它是跟类有关系但在运行时又不需要实例和类参与的方法,可以使用类和实例来调用。

类方法

class A(object):
    bar = 1
    @classmethod
    def class_foo(cls):
        print 'Hello, ', cls
        print cls.bar

>>> A.class_foo()   # 直接通过类来调用方法
Hello,  <class '__main__.A'>
1

注意:类方法可有类或者实例调用,不过定义该方法时要传入cls参数,该参数代表了类本身,通过cls可以调用类中的属性和方法。

静态方法

class A(object):
    bar = 1
    @staticmethod
    def static_foo():
        print 'Hello, ', A.bar

>>> a = A()
>>> a.static_foo()
Hello, 1
>>> A.static_foo()
Hello, 1

静态方法主要是运行时不需要实例和类参与,不需要传入任何参数。

定制类与特殊方法

在python中,经常会看到前后两个下划线的方法。这些方法称为魔法方法或者特殊方法。有特殊的功能,下面是常用的特殊方法:

  • __new__
  • __str__ , __repr__
  • __iter__
  • __getitem__, __setitem__, __delitem__
  • __getattr__, __setattr__, __delattr__
  • __call__

__new__ —— 自定义实例化

创建实例时,调用__new__, 再 __init__对实例(self)进行初始化
- __new__ 是在 __init__ 之前被调用的;
- __new__ 是类方法,__init__ 是实例方法;
- 重载 __new__ 方法,需要返回类的实例;

一般情况下,我们不需要重载 __new__ 方法。但在某些情况下,我们想控制实例的创建过程,这时可以通过重载 __new_ 方法来实现。

class A(object):
    _dict = dict()

    def __new__(cls):
        if 'key' in A._dict:
            print "EXISTS"
            return A._dict['key']
        else:
            print "NEW"
            return object.__new__(cls)

    def __init__(self):
        print "INIT"
        A._dict['key'] = self

在上面,我们定义了一个类 A,并重载了 __new__ 方法:当 key 在 A._dict 中时,直接返回 A._dict[‘key’],否则创建实例。

>>> a1 = A()
NEW
INIT
>>> a2 = A()
EXISTS
INIT
>>> a3 = A()
EXISTS
INIT

__str__ & __repr__ —— 让对象输出信息

简单来说,就是如果直接print一个类的对象的话,就会得到类似 <__main__.Foo object at 0x10c37aa50> 。然而有时候我们就想显示这个类的对象的信息,此时就可以用__str__, 该函数表示,当print这个类的对象时,所输出该对象的信息。

class Foo(object):
    def __init__(self, name):
        self.name = name
    def __str__(self):
        return 'Foo object (name: %s)' % self.name

>>> print Foo('ethan')      # 使用 print, 显示该对象的信息
Foo object (name: ethan)
>>>
>>> str(Foo('ethan'))       # 使用 str
'Foo object (name: ethan)'
>>>
>>> Foo('ethan')             # 直接显示
<__main__.Foo at 0x10c37a490>

你可能会说,哎呀, Foo(‘ethan’) 就没法显示这个对象的信息吗?我看这<__main__.Foo at 0x10c37a490> 东西有啥用啊。__repr__就是让 Foo(‘ethan’)也显示类似加了print的效果。

class Foo(object):
    def __init__(self, name):
        self.name = name
    def __str__(self):
        return 'Foo object (name: %s)' % self.name
    __repr__ = __str__  #重载__repr__方法

>>> Foo('ethan')
'Foo object (name: ethan)'

__iter__ —— 让对象成为迭代器

一句话:对象可用于 for … in 循环,此时要定义 __iter__ 和 next (Python3中是 __next__方法)。__iter__ 返回一个迭代对象,__next__ 返回容器的下一个元素,在没有后续元素时抛出 StopIteration 异常。

class Fib(object):
    def __init__(self):
        self.a, self.b = 0, 1

    def __iter__(self):  # 返回迭代器对象本身
        return self      

    def next(self):      # 返回容器下一个元素
        self.a, self.b = self.b, self.a + self.b
        return self.a    

>>> fib = Fib()
>>> for i in fib:
...     if i > 10:
...         break
...     print i
...
1
1
2
3
5
8

__getitem__ —— 将实例对象看成一项项取值

就是使用obj[n]的方式对实例对象取值

class Fib(object):
    def __getitem__(self, n):
        if isinstance(n, slice):   # 如果 n 是 slice 对象
            a, b = 1, 1
            start, stop = n.start, n.stop
            L = []
            for i in xrange(stop):
                if i >= start:
                    L.append(a)
                a, b = b, a + b
            return L
        if isinstance(n, int):     # 如果 n 是 int 型
            a, b = 1, 1
            for i in xrange(n):
                a, b = b, a + b
            return a


>>> fib = Fib()
>>> fib[0:3]
[1, 1, 2]
>>> fib[2:6]
[2, 3, 5, 8]

getattr —— 对获取不存在的属性的行为进行特殊处理

当我们获取对象的某个属性,如果该属性不存在,会抛出 AttributeError 异常,而__getattr__就能避免这个异常。

class Point(object):
    def __init__(self, x=0, y=0):
        self.x = x
        self.y = y
    def __getattr__(self, attr):
        if attr == 'z':
            return 0
        raise AttributeError("Point object has no attribute %s" % attr)

>>> p = Point(3, 4)
>>> p.z  #只有调用不存在的属性时,\__getattr__才会起作用
0
>>> p.w  
AttributeError: Point object has no attribute w

__call__ —— 让实例直接调用方法

这个比较有意思。一般来说obj.method()来调用对象的方法。 能不能简单点啊,直接obj()来啊。

class Point(object):
    def __init__(self, x, y):
        self.x, self.y = x, y
    def __call__(self, z):
        return self.x + self.y + z

>>> p = Point(3, 4)
>>> callable(p)     # 使用 callable 判断对象是否能被调用
True
>>> p(6)            # 传入参数,对实例进行调用,对应 p.__call__(6)
13                  # 3+4+6

__slots —— 限定实例允许绑定的属性

是是是是,我们可以定义类的时候加上属性和方法,但是有时候我们有了个实例,只想给这个实例加上一些新的属性和方法。。(要求真多。。)当然了,最简单的方法直接弄就行。有时候我们也不希望别人乱加属性。
slots:限定允许绑定的属性

class Point(object):
    __slots__ = ('x', 'y')       # 只允许使用 x 和 y属性

    def __init__(self, x=0, y=0):
        self.x = x
        self.y = y
>>> p = Point(3, 4)
>>> p.z = 5
AttributeError: 'Point' object has no attribute 'z'

如果是下面的情况

class Point(object):
    __slots__ = ('x', 'y','z')       

    def __init__(self, x=0, y=0):
        self.x = x
        self.y = y



p = Point(3,4)        
p.z = 5  #此时可以添加属性z了

一个注意点:__slots__只能限定当前类,不能限定子类。除非子类也定义了 slots,这样,子类允许定义的属性就是自身的 slots 加上父类的 slots。

@property ——设置属性的发生自定义访问

有些属性我们希望对其访问进行限制,比如下面的score, 我们总不能让别人随便设置score的值吧。所以一般要定义两个函数,set_score和get_score分别表示对score的读取和赋值进行限定。 比如:

class Exam(object):
    def __init__(self, score):
        self._score = score

    def get_score(self):
        return self._score

    def set_score(self, val):
        if val < 0:
            self._score = 0
        elif val > 100:
            self._score = 100
        else:
            self._score = val

>>> e = Exam(60)
>>> e.get_score()
60
>>> e.set_score(70)
>>> e.get_score()
70

但你不感觉这样写很麻烦吗?回顾一下C#中的。不是有set和get的访问器吗?
比如下面的通过函数Code的get和set进行自定义限定code属性。

  class Student
  {
     private string code = "N.A";
     // 声明类型为 string 的 Code 属性
     public string Code
     {
        get
        {
           return code;
        }
        set
        {
           code = value;
        }
     }

   }

当然啊,python也有类似的。不过感觉写起来很繁琐。。唉唉
注意:get属性的话直接加上@property装饰器就行,然而set属性的话,就要加上类似@score.setter装饰器。。当然类似的,如果去掉 @score.setter装饰器的话,score就变成只读属性了。还敢说这样写不麻烦。。。

class Exam(object):
    def __init__(self, score):
        self._score = score

    @property
    def score(self):
        return self._score

    @score.setter
    def score(self, val):
        if val < 0:
            self._score = 0
        elif val > 100:
            self._score = 100
        else:
            self._score = val

>>> e = Exam(60)
>>> e.score
60
>>> e.score = 90
>>> e.score
90
>>> e.score = 200
>>> e.score
100

super —— 子类调用父类方法?

class Base(object):
    def __init__(self, a, b):
        self.a = a
        self.b = b

class A(Base):
    def __init__(self, a, b, c):
        super(A, self).__init__(a, b)  # Python3 可使用 super().__init__(a, b)
        self.c = c

从上面可以看出。super貌似是直接在子类中调用父类的方法,从而减少劳动。。
然而如果是下面这样的继承方式。。

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"

      Base
      /  \
     /    \
    A      B
     \    /
      \  /
       C

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

可以看到,进入A之后,再调用super,并不是进入Base,而是进入了B。

MRO列表

事实上,对于你定义的每一个类,Python 会计算出一个方法解析顺序(Method Resolution Order, MRO)列表,它代表了类继承的顺序。mro可以获取类的MRO列表.

>>> C.mro()   # or C.__mro__ or C().__class__.mro()
[__main__.C, __main__.A, __main__.B, __main__.Base, object]

MRO列表的顺序是根据C3线性化算法来定的。

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(C, self).__init__() 的解析方式不就是先C, 再A,再B,最后再Base了。

metaclass

这个还是直接看原文吧

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值