Python语言学习笔记(三)

Python语言学习笔记(三)

面向对象高级编程

__slots__的使用

由于python允许程序动态绑定,如果我们想要限制实例的属性,我们可以在class定义时,定义一个特殊变量__slots__。比如,只允许对Student实例添加name和age属性:

class Student(object):
__slots__ = ('name', 'age') # 用tuple定义允许绑定的属性名称

 

这样,如果在类外动态添加属性,那么会报错。
另外__slots__定义的属性仅对当前类实例起作用,对继承的子类不起作用。

@property的使用

为了将类方法可以像类属性一样的方式来调用,python提供了内置的@property装饰器。来看下函数的API:

property(fget=None, fset=None, fdel=None, doc=None)

 

#1.
class Student(object):
 
def get_score(self):
return self._score
 
def set_score(self, value):
if not isinstance(value, int):
raise ValueError('score must be an integer!')
if value < 0 or value > 100:
raise ValueError('score must between 0 ~ 100!')
self._score = value
 
#2.
class Student(object):
 
@property
def score(self):
return self._score
 
@score.setter
def score(self, value):
if not isinstance(value, int):
raise ValueError('score must be an integer!')
if value < 0 or value > 100:
raise ValueError('score must between 0 ~ 100!')
self._score = value
 
>>> s = Student()
>>> s.score = 60 # OK,实际转化为s.set_score(60)
>>> s.score # OK,实际转化为s.get_score()
60

实质上,这是通过gettersetter方法来实现的。
还可以定义只读属性,只定义getter方法,不定义setter方法就是一个只读属性。

参考:
@property如果要懂property,强烈建议看一下这篇。

定制类

1.__len__()
2.__str__()
by the print statement to compute the “informal” string representation of an object
3.__repr__
by string conversions (reverse quotes) to compute the “official” string representation of an object

#1.
class Student(object):
def __init__(self, name):
self.name = name
def __str__(self):
return 'Student object (name=%s)' % self.name
 
>>> print(Student('Michael'))
Student object (name: Michael)
>>> s = Student('Michael')
>>> s
<__main__.Student object at 0x109afb310>
 
#2.
class Student(object):
def __init__(self, name):
self.name = name
def __str__(self):
return 'Student object (name=%s)' % self.name
__repr__ = __str__
>>> s = Student('Michael')
>>> s
Student object (name: Michael)

 

4.__iter__
如果一个类想被用于for ... in循环,类似list或tuple那样,就必须实现一个__iter__()方法,该方法返回一个迭代对象,然后,Python的for循环就会不断调用该迭代对象的__next__()方法拿到循环的下一个值,直到遇到StopIteration错误时退出循环。

class Fib(object):
def __init__(self):
self.a, self.b = 0, 1 # 初始化两个计数器a,b
 
def __iter__(self):
return self # 实例本身就是迭代对象,故返回自己
 
def __next__(self):
self.a, self.b = self.b, self.a + self.b # 计算下一个值
if self.a > 100000: # 退出循环的条件
raise StopIteration()
return self.a # 返回下一个值
 
>>> for n in Fib():
... print(n)
...
1
1
2
3
5
...
46368
75025

 

5.__getitem__
6.__getattr__
7.__call__

参考:
定制类-廖雪峰

使用元类

type
python中,class的定义是运行时动态创建的,而创建class的方法就是使用type()函数。
type()函数既可以返回一个对象的类型,又可以创建出新的类型。

type(object) -> the object's type
type(name, bases, dict) -> a new type

 

>>> def fn(self, name='world'): # 先定义函数
... print('Hello, %s.' % name)
...
>>> Hello = type('Hello', (object,), dict(hello=fn)) # 创建Hello class
>>> h = Hello()
>>> h.hello()
Hello, world.

要创建一个class对象,type()函数依次传入3个参数:

1.    class的名称;
2.    继承的父类集合,注意Python支持多重继承,如果只有一个父类,别忘了tuple的单元素写法;
3.    class的方法名称与函数绑定,这里我们把函数`fn`绑定到方法名`hello`上。

通过type()函数创建的类和直接写class是完全一样的,因为Python解释器遇到class定义时,仅仅是扫描一下class定义的语法,然后调用type()函数创建出class。

metaclass
除了使用type()动态创建类以外,要控制类的创建行为,还可以使用metaclass
先定义metaclass,就可以创建类,最后创建实例。

# metaclass是类的模板,所以必须从`type`类型派生:
class ListMetaclass(type):
def __new__(cls, name, bases, attrs):
attrs['add'] = lambda self, value: self.append(value)
return type.__new__(cls, name, bases, attrs)

 

正常情况下会很少用到,但是总会遇到需要通过metaclass修改类定义的。ORM就是一个典型的例子。
可以直接去参考廖雪峰的教程

其他

自省

自省是指这种能力:检查某些事物以确定它是什么、它知道什么以及它能做什么。自省向程序员提供了极大的灵活性和控制力。一旦您使用了支持自省的编程语言,就会产生类似这样的感觉:“未经检查的对象不值得实例化。“
1.联机帮助(help())
2.sys模块
3.dir函数
4.文档字符串__doc__
5.检查python对象
名称:__name__,类型:type(),属性:hasattr()getattr(),可调用:callable(),实例isinstance()、子类:issubclass()

参考:Python自省指南

装饰器@staticmethod和@classmethod

@staticmethod和@classmethod都可以直接类名.方法名()来调用(staticmethod也可以用实例名.方法名()来调用),那它们的区别在哪里呢?

class A(object):
bar = 1
 
def foo(self, x):
print ("executing foo(%s,%s)"%(self,x))
 
@classmethod
def class_foo(cls,x):
print ("executing class_foo(%s,%s)"%(cls,x))
print ("bar: %s" % cls.bar)
 
@staticmethod
def static_foo(x):
print ("executing static_foo(%s)"%x)
print ("bar: %s" % A.bar)
 
a = A()
A.static_foo()
A.class_foo()

 

1.@classmethod的第一个参数必须是表示自身类的cls参数。而@staticmethod不需要self也不需要cls参数,就跟使用函数一样。
2.如果在@staticmethod中要调用到这个类的一些属性方法,只能直接类名.属性名或类名.方法名。而@classmethod因为持有cls参数,可以来调用类的属性,类的方法,实例化对象等。
参考:
装饰器@staticmethod和@classmethod有什么区别

__new____init__的区别

__init__方法为初始化方法,__new__方法才是真正的构造函数。
1、__new__方法默认返回实例对象供__init__方法、实例方法使用
2、__init__方法为初始化方法,为类的实例提供一些属性或完成一些动作
3、__new__方法创建实例对象供__init__方法使用,__init__方法定制实例对象。
__new__ 方法必须返回值,__init__方法不需要返回值。(如果返回非None值就报错)
4、一般用不上__new__方法,__new__方法可以用在下面二种情况。

建议看下python类中new 和 init方法区别

python中的单例模式

1.使用模块
2.使用__new__
3.使用装饰器(重点看看)
4.使用元类
请参考Python中的单例模式

python中的copy和deepcopy

1、赋值:简单地拷贝对象的引用,两个对象的id相同。
2、浅拷贝:创建一个新的组合对象,这个新对象与原对象共享内存中的子对象。
常见的浅拷贝有:切片操作、工厂函数如list()、对象的copy()方法、copy模块中的copy函数
3、深拷贝:创建一个新的组合对象,同时递归地拷贝所有子对象,新的组合对象与原对象没有任何关联。虽然实际上会共享不可变的子对象,但不影响它们的相互独立性。常见的深拷贝方式,只有copy模块中的deepcopy函数。

import copy
a = [1, 2, 3, 4, ['a', 'b']]  #原始对象
 
b = a  #赋值,传对象的引用
c = copy.copy(a)  #对象拷贝,浅拷贝
d = copy.deepcopy(a)  #对象拷贝,深拷贝
 
a.append(5)  #修改对象a
a[4].append('c')  #修改对象a中的['a', 'b']数组对象
 
print 'a = ', a
print 'b = ', b
print 'c = ', c
print 'd = ', d
 
输出结果:
a =  [1, 2, 3, 4, ['a', 'b', 'c'], 5]
b =  [1, 2, 3, 4, ['a', 'b', 'c'], 5]
c =  [1, 2, 3, 4, ['a', 'b', 'c']]
d =  [1, 2, 3, 4, ['a', 'b']]

请参考赋值、浅拷贝、深拷贝

python的垃圾回收机制GC(Garbage Collection)

Python GC主要使用引用计数(reference counting)来跟踪和回收垃圾。在引用计数的基础上,通过“标记-清除”(mark and sweep)解决容器对象可能产生的循环引用问题,通过“分代回收”(generation collection)以空间换时间的方法提高垃圾回收效率。

可参考Python垃圾回收机制

posted @ 2017-07-26 19:54  天涯海角路  阅读(129)  评论(0)    收藏  举报