这篇博客主要是阅读python之旅 时做的笔记。提取出最主要的知识点,供个人在以后中快速查阅。
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 "" , line 1, in
AttributeError: 'Animal' object has no attribute '__name'
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
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__, 再 __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
简单来说,就是如果直接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)'
一句话:对象可用于 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
就是使用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]
当我们获取对象的某个属性,如果该属性不存在,会抛出 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
这个比较有意思。一般来说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:限定允许绑定的属性
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。
有些属性我们希望对其访问进行限制,比如下面的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
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。
事实上,对于你定义的每一个类,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线性化算法来定的。
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了。
这个还是直接看原文吧