首页 > 编程 > Python > 正文

Python基础IV---类

2019-11-09 19:09:21
字体:
来源:转载
供稿:网友

这篇博客主要是阅读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.nameTraceback (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/setattrdir(obj)

继承和多态

class Animal(object): def __init__(self, name): self.name = name def greet(self): print 'Hello, I am %s.' % self.nameclass 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.nameclass Dog(Animal): def greet(self): print 'WangWang.., I am %s.' % self.nameclass Cat(Animal): def greet(self): print 'MiaoMiao.., I am %s' % self.namedef 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()NEWINIT>>> a2 = A()EXISTSINIT>>> a3 = A()EXISTSINIT

__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...112358

__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 = 5AttributeError: '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 = yp = 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.score60>>> e.score = 90>>> e.score90>>> e.score = 200>>> e.score100

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

class Base(object): def __init__(self, a, b): self.a = a self.b = bclass 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 Center Aenter Benter Baseleave Baseleave Bleave Aleave 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

这个还是直接看原文吧


发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表