这一章突然卡壳了,翻来覆去看了好几遍,可能是对类,多态不太熟悉清楚的缘故,现在做一些笔记。
- 在Python中创建一个构造方法:
只要把init方法的名字从简单的init修改为魔法版本_ _ init _ _即可:
>>> class FooBar:
def __init__(self):
self.somevar=42
>>> f=FooBar()
>>> f.somevar
42
_ _ init _ _ 构造函数
_ _ del _ _ 析构函数
- 重写一般方法和特殊的构造方法:
构造方法用来初始化新创建对象的状态,大多数子类不仅要拥有自己的初始化代码,还要拥有超类的初始化代码。虽然重写的机制对于所有方法来说都是一样的,但是当处理构造方法比重写普通方法时,更可能遇到特别的问题:如果一个类的构造方法被重写,那么就需要调用超类(你所继承的类)的构造方法,否则对象可能不会被正确地初始化。
class bird():
def __init__(self):
self.a = 0
class bird_1(bird):
def __init__(self):
bird.__init__(self)
self.sound = "haha"
这里为了调用父类的构造函数,用到了未绑定的父类的构造方法。
1. 绑定方法:即用 对象 . 方法 的形式来调用,每个方法都有一个self参数指向调用他的对象,在对象引用这个方法的时候,该方法就绑定到了这个对象上。
2. 非绑定方法:因为方法的定义是在类中,也就是说,该方法的命名空间是类域。那么用命名空间。方法的形式去调用一个方法显然是不绑定任何对象的,但是这样一来我们就要为这个方法显式的制定self参数,不然它不会知道要对哪个对象的特性进行操作。
- 除了利用非绑定方法来调用父类中的构造函数意外,在新类中,还可以用supper函数来实现这个功能
super():
当前子类和对象都可以作为super的函数来调用,该函数会返回一个supper对象,再次之后再调用任何方法和特性,都是针对该类的所有父类进行搜寻要调用的特性。
class bird():
def __init__(self):
self.a = 0
class bird_1(bird):
def __init__(self):
supper(bird_1, self).__init__()
self.sound = "haha"
应该尽量使用super,因为super在即使基础多个超类时也只需使用一次super函数。
基本的序列和映射规则:
1.其实序列和字典这些Python的内建类型所使用的方法都是所谓的“魔法方法”,这些方法都是python在内部定义过的。一个统一的格式应该是方法名前后都有两个下划线吧。比如在列表这个内建类型中,如果相求一个列表的元素个数,应该用的是len(a).此时的这个len函数就是魔法函数中的_ _ len _ _(self).
2.如果自己想定义一个特殊的类型,而大部分的特性又和列表相关,那么就可以用继承的方式,省去实现很多系统中早已经定义好的方法。
3.访问器方法实际上就是在访问类中特性的同时还要对特性做一些操作的方法,并且在封装和私有化上有着不小的作用(python中没有真正的私有化)
4.为了让代码写的更加简洁一点,便不会再类中定义了很多构造函数来满足特性的访问和修改的工作,而是用一种叫做访问器隐藏的方法——Property。(见下方介绍)序列和映射是对象的集合。为了实现它们基本的行为(规则),如果对象是不可变的,那么就需要使用两个魔法方法,如果是可变的则需要使用4个。
_ _ len _ _(self):这个方法应该返回集合中所含项目的数量。对于序列来说,这就是元素的个数。对于映射来说,则是键-值对的数量。
_ _ getitem _ _(self,key):这个方法返回与所给键对应的值。对于一个序列,键应该是1个0~n-1的整数(或者像后面所说的负数),n是序列的长度;对于映射来说,可以使用任何种类的键。
_ _ setitem _ _(self,key,value):这个方法应该按一定的方式存储和key相关的value,该值随后可使用getitem来获取。当然,只能为可以修改的对象定义这个方法。
_ _ delitem _ _(self,key):这个方法在对一部分对象使用del语句时被调用,同时必须删除和元素相关的键。这个方法也是为可修改的对象定义的(并不是删除全部的对象,而只删除一些需要移除的元素)。
- python允许子类化内建类型,比如list:
>>> class CounterList(list):
def __init__(self,*args):
super(CounterList,self).__init__(*args)
self.counter=0
def __getitem__(self,index):
self.counter +=1
return super(CounterList,self).__getitem__(index)
- property:
这个函数会生成一个属性,将访问器函数作为参数。
class test()
{
def __init__(self):
self.name=""
self.age=0
def get_num(self):
return self.name, self.age
def set_num(self, size):
self.name, self.age = size
size = property(get_num, set_num)
}
s = test() //创建对象并初始化
s.name = "xiaoming"
s.age = 18
s.size()
s.size = ("xiaofang", 8)
- 静态方法和类成员方法:
静态成员方法和类成员方法就相当于是非绑定方法和绑定方法。静态成员方法和类成员方法分别装入Staticmethod类型和Classmethod类型的对象中,静态方法没有self参数,能够被类本身直接调用。类方法在定义时需要cls参数,类似于self。类成员方法可以直接用类的具体对象调用,但cls参数是自动被绑定到类的。
class test()
{
def __init__(self):
self.name=""
self.age=0
def get_num(self):
get_num=staticmethod(get_num) #声明为静态成员方法
return self.name, self.age
def set_num(self, size):
self.name, self.age = size
def print_lol(cls):
print "hello world"
print_lol = classmethod(print_lol) #声明为类成员方法
size = property(get_num, set_num)
}
也可以用装饰器:
class test()
{
def __init__(self):
self.name=""
self.age=0
@staticmethod
def get_num(self):
return self.name, self.age
def set_num(self, size):
self.name, self.age = size
@classmethod
def print_lol(cls):
print "hello world"
size = property(get_num, set_num)
}
魔法方法其实就是python中已经定义好了的一些方法。他们不会被显式的调用,一般在创建对象,或者执行一些访问特性的操作的时候,他们在类中会自动的隐式调用。比如,你设定了一个列表,那么当你用索引值引用列表中的某一个元素的时候,比如a[4],那么在list这个类里面就会自动调用_ _ getattr _ _这样的魔法方法,因为这个元素被看错是一个特性,你在访问特性的同时,还要对特性进行操作。所以,魔法方法看起来很抽象,但是我们无时不刻不在使用它。
_ _ getattr_ _ 和_ _ setattr_ _:
函数 | 作用 |
---|---|
_ _ getattribute_ _(self, name) | 当特性name被访问时自动调用(只能在新类中使用) |
_ _ getattr_ _(self,name) | 当特性name被访问且对象没有相应特性时自动调用 |
_ _ setattr_ _(self,name,value) | 当试图给特性name赋值时会自动调用 |
_ _ delattr _ _(self, name) | 当试图删除特性name时会被自动调用 |
迭代器
定义:具有next()方法的对象
使用next,迭代器会返回他的下一个值,如果这个值不存在还调用了一个next,那么他会抛出一个异常。
迭代器大多数用来迭代可以迭代的对象, 但是以前的迭代大多数用了列表和字典等数据结构,使用迭代器的好处就是能够尽量的缩短对空间的消耗,不会像列表一样,一次行把要迭代的元素全都放入内存,_ _ iter _ 会返回一个迭代器本身,在循环中,同样可以迭代其中的所有的元素。比如,算斐波那契数列的话,如果直接用列表来迭代(假设要无限迭代),那么必然需要一个无限大的列表,如果用迭代器的话就不会。当一个类中有实现 _ iter _ 的话,那么这个类的对象就是可迭代的,如果还实现了 _ next_ _ ,那么这个类实例化的对象就是一个迭代器。生成器
包含任何yield语句的函数都被称做是一个生成器。可以把它看做是和return 语句的性质相同,只不过它不会就此终止程序并返回一个值,而是每次执行到此处时,产生一个值,变为阻塞状态,等待下一次被激活,继续运行。