1,两大思想
- 编程的两大思想:面向过程,面向对象
面向过程(小方向,细节操作)
面向对象(大方向,宏观过程)
- 类与对象
(1)类:类别,分门别类,物以类聚人类,鸟类,动物类,植物类…… 【用 class 声明】
·类,是多个类似事物组成的群体的统称。能够帮助我们快速理解和判断事物的性质
(2)类 —— 数据类型
· 不同的数据类型属于不同的类
· 使用内置函数查看数据类型 print(type(100)) —— <class ‘int’>
(3)对象
·100、99、520都是int类之下包含的相似的不同个例,这个个例专业数语称为“实例或对象”【int类下的具体对象】
(4)Python 中一切皆对象
-
定义Python中的类
(1)创建类的语法class Student:
pass (#用于占位)
#Student为类的名称,简称类名【规范:由一个或者多个单词组成,要求每个单词首字母大写,其余小写】
(2)类的组成
· 类属性
· 实例方法 (self)
· 静态方法 ()
· 类方法 (cls)
#在类之外定义的称为函数,在类之内定义的称为方法
```python
print('--------------------107.定义Python中的类------------------')
class Student: #Student为类的名称,简称类名,【规范:由一个或者多个单词组成,要求每个单词首字母大写,其余小写】
pass #用于占位
#Python 中一切皆对象,Student是对象吗?内存有开空间吗?
print(id(Student)) #2092255264000
print(type(Student)) #<class 'type'>
print(Student) #<class '__main__.Student'>
print('类的组成: 类属性 实例方法 静态方法 类方法')
class Student: #Student为类的名称,简称类名,【规范:由一个或者多个单词组成,要求每个单词首字母大写,其余小写】
native_pace='吉林' #直接卸载类里的变量,称为类属性
def __init__(self,name,age): #初始化方法 def __init__(self):
self.name=name # self.name 称为实体属性,进行了赋值操作,将局部变量 name 的值赋给实体属性·
self.age=age
#实例方法
def eat(self): #括号内一般自带self
print('学生在吃饭...')
# 类方法
@classmethod
def cm(cls):
print('使用了classmethod进行修饰,所以为 类方法')
# 静态方法
@staticmethod
def method(): #括号内不写self
print('使用了staticmethod进行修饰,所以为 静态方法')
#在类之外定义的称为函数,在类之内定义的称为方法
def drink():
print('喝水')
--------------------107.定义Python中的类------------------
2865934802592
<class 'type'>
<class '__main__.Student'>
类的组成: 类属性 实例方法 静态方法 类方法
108. 对象的创建
(1)对象的创建又称为: 类的实例化
(2)语法:
实例名=类名()
#实例名 即 对象名
(3)例子
print('--------------------108.对象的创建------------------')
#接107节,创建Student的对象
stu1=Student('张三',20)
print(id(stu1)) #标识
print(type(stu1)) #类型
print(stu1) #值,内存地址是一个十六进制的数
stu1.eat() #调用类的方法 # 对象名.方法名
print(stu1.name)
print(stu1.age)
print('--------')
Student.eat(stu1) # 与stu1.eat() 的代码功能相同,都是调用Student类中的eat方法
#类名.方法名(类的对象-->方法定义处的self)
--------------------108.对象的创建------------------
2865938204368
<class '__main__.Student'>
<__main__.Student object at 0x0000029B473A86D0>
学生在吃饭...
张三
20
--------
学生在吃饭...
109. 类属性/类方法/静态方法的使用方式
·类属性:类里 and 方法外的变量称为类属性,被该类的所有对象所共享
·类方法:使用@classmethod修饰的方法,使用类名直接访问的方法
·静态方法:使用@staticmethod修饰的主法,使用类名直接访问的方法
Note:实例方法的self指的是实例对象,类方法的cls指的是类的元数据(类的自身)
以上三者都通过,类名加. 去使用 à stu1.xxx
而实例方法,通过对象名加. 去使用,或者 类名加. 去使用
print('--------------------109.类属性/类方法/静态方法的使用方式------------------')
class Student: #Student为类的名称,简称类名,【规范:由一个或者多个单词组成,要求每个单词首字母大写,其余小写】
native_place='吉林' #直接写在类里的变量,称为【类属性】
def __init__(self,name,age): #初始化方法 def __init__(self):
self.name=name # self.name 称为实体属性,进行了赋值操作,将局部变量 name 的值赋给实体属性·
self.age=age
#实例方法
def eat(self): #括号内一般自带self --> 要求传入Student 中的一个对象
print('学生在吃饭...')
# 类方法
@classmethod
def cm(cls): #cls调用时不需要传入
print('使用了classmethod进行修饰,所以为 类方法')
# 静态方法
@staticmethod
def method(): #括号内没有任何默认参数
print('使用了staticmethod进行修饰,所以为 静态方法')
print('---类属性的使用方式---')
print(Student.native_place) #使用类名调用
stu1=Student('张三',20)
stu2=Student('李四',30)
print(stu1.native_place)
print(stu2.native_place)
Student.native_place='天津' #类属性 被 实例属性共享
print(stu1.native_place)
print(stu2.native_place)
print('---类方法使用方式---')
Student.cm() #使用类名直接调用
print('---静态方法使用方式---')
Student.method() #使用类名直接调用
--------------------109.类属性/类方法/静态方法的使用方式------------------
---类属性的使用方式---
吉林
吉林
吉林
天津
天津
---类方法使用方式---
使用了classmethod进行修饰,所以为 类方法
---静态方法使用方式---
使用了staticmethod进行修饰,所以为 静态方法
110. 动态绑定属性和方法
·Python是动态语言,在创建对象之后,可以动态地绑定属性和方法
print('--------------------110.动态绑定属性和方法------------------')
class Student:
def __init__(self,name,age):
self.name = name # self.name 称为实例变量,进行了赋值操作,将局部变量 name 的值赋给实例变量·
self.age = age
def eat(self):
print(self.name+'在吃饭')
#创建学生类的对象
stu1=Student('张三',20)
stu2=Student('李四',30)
print(id(stu1))
print(id(stu2))
print('--------------')
stu1.gender='女' #动态绑定属性
print(stu1.name,stu1.age,stu1.gender)
#print(stu2.name,stu2.age,stu2.gender) #'Student' object has no attribute 'gender'
print('--------------')
stu1.eat()
stu2.eat()
def show():
print('定义在类之外的,称为’函数‘')
stu1.show=show() #动态绑定方法
stu1.show
print('--------------------Over------------------')
--------------------110.动态绑定属性和方法------------------
2865942730256
2865942731072
--------------
张三 20 女
--------------
张三在吃饭
李四在吃饭
定义在类之外的,称为’函数‘
--------------------Over------------------
# 2,类和对象的创建
class Student(类名) :
类名由一个或者多个单词组成,每个单词的首字母要大写.其余小写
```python
#类与对象
class Student :
naplace='河南'
def __init__(self,name,age):
self.name=name
self.age=age
#Java里面类有默认的构造器,可以构造里面的类的参数。但是python需要指明
#实例方法,在类里面定义的函数叫做方法,在类外面定义的称为函数
def f(self):
print('学生再吃饭')
@staticmethod
def f1():
print('使用静态方法')
#类方法
@classmethod
def f2(cls):
print('我使用了类方法')
def drink():
print('喝水')
```python
```python
```python
```python
#对象的创建
s1=Student('毛腾凯',19)
print(id(s1))#
print(s1.name,s1.age)##毛腾凯 19
s1.f2()#我使用了类方法
s1.f1()#使用静态方法
s1.f()#学生再吃饭
Student.f1()
#Student.f()#TypeError: f() missing 1 required positional argument: 'self'
#只有静态方法,类方法调用时可以直接类名.方法,如果其他也想用,则需要在里面填入对象名
Student.f(s1)
print('------------------------------------')
s2=Student('臧东坤',18)
s2.gender='女'#
print(s2.name+'的性别是'+s2.gender)#臧东坤的性别是女
def show():
print('爱唱歌')
s2.show=show
s2.show()#爱唱歌
类属性:类中方法外的变量称为类属性,被改类的所有对象所共享
类方法:使用@calssmethod修饰的方法,使用类名直接访问的方法
静态方法:使用@staticmethod修饰的方法,使用类名直接访问的方法
一个类可以创建需对个对象,每个对象的属性值可以不同
init方法中定义的属性在所有的对象中都有。
动态绑定方法与属性
定义在类之外的方法,可以动态的绑定为对象的一个方法,记住是对象,不是类,是其他对象没有的方法 :对象.方法名=需要绑定的方法名
动态绑定属性:对象.属性名=属性的值
3,封装
封装:提高程序的安全性
将数据属性与行为(方法)包装到类对象中,在方法内部对属性进行操纵,在类对象的外部调用方法,这样无需关心方法内部具体实现细节,从而隔离复杂度。
在python中没有专门的修饰私有的,如果改属性不希望再类对象外部被访问,前面使用两个’_’
#类的创建与对象的创建
class People:
pifu="黄色"
#实例方法
def __init__(self,name,age):
self.name=name
self.__age=age
@classmethod
def eat(cls):
print('喜欢吃奥利给')
@staticmethod
def z():
print('我的名字叫'+name+'我今年'+__age)
p1=People('毛',19)
print(p1.pifu)
print(p1.name)
#print(p1.__age)#在类的外部不能访问带‘__’的属性,
# #AttributeError: 'People' object has no attribute '__age'
print(p1._People__age)#19这种方法可以看到,对象._类名属性,
# 但是一般不提倡用这种,因为这也想是规矩,靠自觉
print(People.eat())
4,继承
提高代码的复用性
class 子类类名(父类,,,,)
如果一个类没有继承任何类,则默认继承object
Python支持多继承
定义子类时,必须在器构造函数中调用父类的构造函数
#继承
class Person(object):
def __init__(self,name,age):
self.name=name
self.age=age
def info(self):
print('我的名字叫做%s,我的年级为%s' % (self.name,self.age))
p1=Person('毛腾凯',19)
p1.info()
class Student(Person) :
def __init__(self,name,age,score):
super(Student, self).__init__(name,age)
self.score=score
s1=Student('宋梦肖','20',100)
print(s1.info())
print(s1.score)
5,方法重写
调用之前写过的
super().方法名()
class Student:
def __init__(self,name,age):
self.name=name
self.age=age
class Person:
def __init__(self,name,age):
self.name=name
self.age=age
def __str__(self):
return '我的名字叫做{0},我的年龄是{1}'.format(self.name,self.age)
s1=Student('毛腾凯','20')
print(s1)
#<__main__.Student object at 0x0000024A0C938700>
p1=Person('毛腾凯','19')
print(p1)
#我的名字叫做毛腾凯,我的年龄是19
6,object类
内置函数dir()可以查看指定队形的所有属性
如果不·对·str()进行重写,则直接输出的是对象的地址
def ——str——(self):
pass
1.基本介绍
object类有一个__str__()方法,用于返回一个"对象的描述",对应于内置函数str(),经常用于print()方法,帮助我们查看对象的信息。
因为__str__()默认在object类中定义,所以子类可以重写这个方法。
不同语言中对应的不同的方法名:
Python语言: str()方法
JAVA语言:toString()方法
PHP语言:__toString()方法
2.方法定义
str()默认定义在类object当中。
def str(self, *args, **kwargs): # real signature unknown
“”" Return str(self). “”"
pass
3.简单示例
示例代码:
class Student(object):
pass
class Person(object):
def init(self, name, age):
self.name = name
self.age = age
def str(self):
return “名字:{0},年龄:{1}”.format(self.name, self.age)
s1 = Student()
print(s1)
print(“–” * 20)
p1 = Person(“聂发俊”, 100)
print(p1)
运行结果:
----------------------------------------
名字:聂发俊,年龄:100
程序说明:
类Student直接继承类object,所以__str__()也直接沿用,所以打印内容格式如下:的格式
类Person继承类object,但是定义了__str__(),属于子类重写父类方法,采用子类的__str__()方法,所以执行打印的时候,输出:名字:聂发俊,年龄:100.
7,多态
提高程序·的可扩展性和课维护性
8,特殊方法与特殊属性
#dir(显示类中所有属性以及方法)
print(dir(object))
#['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__',
# '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
# '__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '
# __reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__',
# '__str__', '__subclasshook__']
class A:
pass
class B:
pass
class C(A,B):
def __init__(self,name,age):
self.name=name
self.age=age
x=C('Jace',20)
#以字典形式显示类或者对象中的属性以及值
print(x.__dict__)
#{'name': 'Jace', 'age': 20}
print(C.__dict__)
#{'__module__': '__main__', '__init__':
# <function C.__init__ at 0x00000167D05A80D0>, '__dict__': <attribute
# '__dict__' of 'C' objects>, '__weakref__': <attribute '__weakref__'
# of 'C' objects>, '__doc__': None}
#输出对象所属的类型
print(x.__class__)
#<class '__main__.C'>
#C类父类类型的元组
print(C.__bases__)
#(<class '__main__.A'>, <class '__main__.B'>)
#类都层次结构
print(C.__mro__)
#(<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>)
——new——
–init–():用于初始化对象属性
117集没看懂
构造方法
构造方法是一个特殊方法。当一个对象被创建后,会立即调用构造方法。
创建构造方法:
class FooBar:
def __init__(self):
self.somevar = 42
f = FooBar()
print f.somevar #42
特殊方法
有的名称前后都有两个下划线,如__future__
,由这些名字组成的集合所包含的方法称为特殊方法。
构造方法
构造方法是一个特殊方法。当一个对象被创建后,会立即调用构造方法。
创建构造方法:
class FooBar:
def __init__(self):
self.somevar = 42
f = FooBar()
print f.somevar #42
123456
也可以在构造方法中传入参数:
class FooBar:
def __init__(self,value=42):
self.somevar = value
123
子类重写父类的方法
如果一个方法在B类的一个实例中被调用(或一个属性被访问),但在B类中没有找到,那么就会去超类A中去找:
class A:
def hello(self):
print "Hello,I'm A."
class B(A):
pass
if __name__ == '__main__':
a = A()
b = B()
a.hello() #Hello,I'm A.
b.hello() #Hello,I'm A.
123456789101112
也可重写一些父类的方法:
class A:
def hello(self):
print "Hello,I'm A."
class B(A):
def hello(self):
print "Hello,I'm B."
if __name__ == '__main__':
a = A()
b = B()
a.hello() #Hello,I'm A.
b.hello() #Hello,I'm B.
12345678910111213
如果一个类的构造方法被重写,那么就需要调用超类的构造方法。
# -*- coding: utf-8 -*
class Bird:
def __init__(self):
self.hungry = True
def eat(self):
if self.hungry:
print 'Aaaah...'
self.hungry = False
else:
print 'No,thanks!'
if __name__ == '__main__':
b = Bird()
b.eat() #Aaaah...
b.eat() #No,thanks!
12345678910111213141516
这个类定义了所有鸟类都具有的基本能力——吃。
class Bird:
def __init__(self):
self.hungry = True
def eat(self):
if self.hungry:
print 'Aaaah...'
self.hungry = False
else:
print 'No,thanks!'
class SongBird(Bird):
def __init__(self):
self.sound = 'Squawk!'
def sing(self):
print self.sound
if __name__ == '__main__':
sb = SongBird()
sb.sing()# Squawk!
1234567891011121314151617181920
我们增加了一个子类,为它添加唱歌的行为。它继承了eat方法:
Traceback (most recent call last):
File "D:/workspace/python/LearnPython/SpecialMethod/Bird.py", line 22, in <module>
sb.eat()
File "D:/workspace/python/LearnPython/SpecialMethod/Bird.py", line 7, in eat
if self.hungry:
AttributeError: SongBird instance has no attribute 'hungry'
123456
但是如果调用会报错。异常说SongBird没有hungry特性(attribute)。
为了达到预期的效果,SongBird
的构造方法必须调用其超类Bird
的构造方法来确保进行基本的初始化。
可以通过调用超类构造方法的未绑定版本或使用super函数。
调用未绑定的超类构造方法
class Bird:
def __init__(self):
self.hungry = True
def eat(self):
if self.hungry:
print 'Aaah...'
self.hungry = False
else:
print 'No,thanks!'
class SongBird(Bird):
def __init__(self):
Bird.__init__(self) # 调用父类构造方法
self.sound = 'Squawk!'
def sing(self):
print self.sound
sb = SongBird()
sb.sing() #Squawk!
sb.eat() #Aaah...
sb.eat() #No,thanks!
123456789101112131415161718192021
调用一个实例的方法时,该方法的self参数会自动绑定到实例上,这称为绑定方法。
如果直接调用类的方法,如Bird.__inint__
,那么就没有实例会被绑定,就要提供需要的self参数,这样的方法称为未绑定方法。
使用super函数
在Python3.0中推荐使用super函数。
__metaclass__=type # super函数只在新式类中起作用
class Bird:
def __init__(self):
self.hungry = True
def eat(self):
if self.hungry:
print 'Aaaah...'
self.hungry = False
else:
print 'No,thanks!'
class SongBird(Bird):
def __init__(self):
super(SongBird,self).__init__() # 注意super括号里面是逗号
self.sound = 'Squawk!'
def sing(self):
print self.sound
if __name__ == '__main__':
sb = SongBird()
sb.sing()# Squawk!
sb.eat()
sb.eat()
123456789101112131415161718192021222324
成员访问
可以创建行为类似序列或映射的对象。
基本的序列和映射规则
序列和映射是对象的集合。为了实现它们的基本行为(规则),如果对象是不可变的,需要两个魔法方法;可变的则需要四个。
__len__(selft)
:返回集合中所含元素的数量__getitem__(self,key)
:返回与所给定键(序列:索引;映射:key)对应的值__setitem__(self,key,value)
:存储key和相关的value__delitem__(self,key)
:在使用del时,同时删除key和value
我们来应用下这些规则:
# -*- coding: utf-8 -*
class ArithmeticSequence:
def __init__(self, start=0, step=1):
"""
初始化算术序列
:param start: 序列中的第一个值
:param step: 两个相邻值之间的差值
_changed : 修改的值的字典
"""
self._start = start
self._step = step
self._changed = {}
def __getitem__(self, key):
"""
从算术序列中返回一项(key,value)
:param key:
:return:
"""
self.checkIndex(key)
try:
return self._changed[key] # 修改了则返回
except KeyError:
return self._start + key * self._step # 计算值
def __setitem__(self, key, value):
"""
修改算术序列中的一个项
:param key:
:param value:
"""
self.checkIndex(key)
self._changed[key] = value
def checkIndex(self, key):
if not isinstance(key, (int, long)): raise TypeError
if key < 0: raise IndexError
if __name__ == '__main__':
s = ArithmeticSequence(1,2)
print s[4] # 需要实现__getitem__
s[4] = 2 #__setitem__
print s[4],s[5]
del s[4] # 报错:ArithmeticSequence instance has no attribute '__delitem__'
123456789101112131415161718192021222324252627282930313233343536373839404142434445
这些魔法方法挺有用的,尤其是你想实现自己的集合类时。
子类化列表,字典和字符串
如果希望实现一个和内建列表行为相似的序列,可以继承list来实现。
当继承一个内建类型时,也就间接地继承了object。因此该类就自动成为新式类,意味着可以使用super函数这样的特性了
# -*- coding: utf-8 -*
class CounterList(list):
def __init__(self,*args):
super(CounterList,self).__init__(*args)
self.counter = 0 #增加counter特性
def __getitem__(self, index):
self.counter += 1
return super(CounterList,self).__getitem__(index)
if __name__ == '__main__':
cl = CounterList(range(10))
print cl #[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
cl.reverse()
print cl #[9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
del cl[3:6]
print cl #[9, 8, 7, 3, 2, 1, 0]
print cl[4] + cl[2] #9 这里调用了两次getitem
print cl.counter #2
1234567891011121314151617181920
CounterList
在很多方面和列表一样,但它有一个counter
特性,每次访问时都会自增。
属性
如果在访问给定特性时徐必须要采取一些行动,那么像这样的封装状态变量(特性)就很重要,如下面的例子:
class Rectangle:
def __init__(self):
self.width = 0
self.height = 0
def setSize(self,size): # 访问器方法
self.width,self.height = size
def getSize(self): # 访问器方法
return self.width,self.height
if __name__ == '__main__':
r = Rectangle()
r.width = 10
r.height = 5
print r.getSize() #(10, 5)
r.setSize((150,100))
print r.width #150
12345678910111213141516
上面的例子中,getSize
和setSize
是一个名为size
的假想特性的访问器方法。size
是由width
和height
构成的元组。这种方式有缺陷,当使用这个类时,不应该要考虑它是怎么实现的(这里是通过元组)。
Python能隐藏访问器方法,让所有特性(attribute)看起来一样。这些通过访问器定义的特性被称为属性(property)。
python中的类成员变量称为特性,作为一Java开发人员有点不习惯啊
property函数
使用property函数可以创建属性。
# -*- coding: utf-8 -*
__metaclass__ = type #property函数只在新式类中使用,不加会最后的r.width打印10
class Rectangle:
def __init__(self):
self.width = 0
self.height = 0
def setSize(self, size): # 访问器方法
self.width, self.height = size # 元组的赋值
def getSize(self): # 访问器方法
return self.width, self.height
size = property(getSize, setSize)
if __name__ == '__main__':
r = Rectangle()
r.width = 10
r.height = 5
print r.size # (10, 5)
r.size = 150, 100
print r.width # 150
1234567891011121314151617181920212223242526
property函数创建了一个属性,其中访问器函数被用作参数(先是取值,然后是赋值),这个属性命名为size。
property()函数中的两个函数分别对应的是获取属性的方法、设置属性的方法,这样一来,外部的对象就可以通过访问size的方式,来达到获取、设置的目的。
当需要更改上例中的getSize、setSize函数的名称时,如果这些方法是作为接口让用户调用的,那么对用户而言就要修改自己调用的方法名,很麻烦,使用了proprty()后,用户就不需担心这种问题了。
静态方法和类成员方法
静态方法和类成员方法分别在创建时装入Staticmethod类型和Classmethod类型的对象中。静态方法的定义没有self参数,能被类本身直接调用。类方法在定义时需要名为cls的参数,类成员方法可以直接用类的具体对象调用。
__metaclass__ = type
class MyClass:
@staticmethod
def smeth():
print 'This is a static method'
@classmethod
def cmeth(cls):
print 'This is a class method of',cls
MyClass.smeth() #This is a static method
MyClass.cmeth() #This is a class method of <class '__main__.MyClass'>
1234567891011121314t
上面的代码中没有实例化类。
__getattr__
、__setattr__
等方法
拦截对象的所有特性访问时可能的。为了在访问特性的时候可以执行代码,必须使用一些魔法方法。
__getattribute__(self,name)
:当特性name被访问时自动被调用(新式类)__getattr__(self,name)
:当特性name被访问且对象没有相应的特性时被自动调用__setattr__(self,name,value)
:当试图给特性name赋值时会被自动调用__delattr__(self,name)
:当试图删特性name时被自动调用
class Rectangle:
def __init__(self):
self.width = 0
self.height = 0
def __setattr__(self, name, value):
if name == 'size':
self.width,self.height = value
else:
self.__dict__[name] = value #__dict__中是所有实例的属性
def __getattr__(self, name):
if name == 'size':
return self.width,self.height
else:
raise AttributeError
if __name__ == '__main__':
r = Rectangle()
r.size = 10,5
print r.size # (10, 5)
r.width = 100
r.height = 50
print r.size #(100, 50)
123456789101112131415161718192021222324
迭代器
这里只讨论一个特殊方法——__iter__
迭代器规则
只要对象实现了__iter__
方法就可以进行迭代。该方法会返回一个迭代器,可以用在for循环中。
使用迭代器实现的斐波那契数列:
class Fibs:
def __init__(self):
self.a = 0
self.b = 1
def next(self): #实现了next方法的对象是迭代器
self.a,self.b = self.b,self.a + self.b
return self.a
def __iter__(self): #实现了__iter__方法的对象是可迭代的
return self
123456789
每次访问next的时候生成下一个值:
fibs = Fibs()
for f in fibs:
if f > 1000:
print f #1597
break
12345
从迭代器得到序列
迭代器和可迭代对象还能转换为序列,比如使用list构造方法显示地将迭代器转换为列表:
class TestIterator:
value = 0
def next(self):
self.value += 1
if self.value>10 : raise StopIteration #用于结束迭代
return self.value
def __iter__(self):
return self
ti = TestIterator()
print list(ti) # [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
1234567891011
生成器
生成器是一种用普通的函数语法定义的迭代器。
创建生成器
创建生成器和创建函数类似,任何包含yield(ES6中也有这个关键字)语句的函数称为生成器。以一个例子来讲解。
首先创建一个列表的列表:
nested = [[1,2,3],[4,5],[6]]
1
然后写一个生成器:
def flatten(nested):
for sublist in nested:
for element in sublist:
yield element # 每产生一个值,函数就会被冻结
1234
每次访问到yield语句时,产生一个值,并且函数被冻结,等待被重新唤醒。函数被重新唤醒后就从停止的那点开始执行。
下面在生成器上迭代来使用所有的值:
for num in flatten(nested):
print num
12
输出:
1
2
3
4
5
6
123456
也可以通过list函数来访问:
print list(flatten(nested)) #[1, 2, 3, 4, 5, 6]
1
生成器推导式返回的是生成器,并且不会立刻进行循环:
>>>g = ((i+2)**2 for i in range(2,27))
>>>g.next()
16
>>>g.next()
25
12345
递归生成器
如果想要处理任意层的嵌套,应该使用递归。
def flatten(nested):
try:
for sublist in nested: #可迭代的情况
for element in flatten(sublist):
yield element
except TypeError: # 和不可迭代的情况
yield nested
print list(flatten([[[1],2],3,4,[5,[6,7,[8,9]]]])) #[1, 2, 3, 4, 5, 6, 7, 8, 9]
123456789
这样做有一个问题:如果nested是一个类似于字符串的对象,那么会导致无穷递归。因为一个字符串的第一个元素是另一个长度为1的字符串,而长度为1的字符串的第一个元素就是字符串本身。
我试了一下,会抛出异常RuntimeError: maximum recursion depth exceeded
def flatten(nested):
try:
#不要迭代类似字符串的对象
try: nested + ''
except TypeError: pass #nested + ''产生的异常被忽略,直接到for语句
else: raise TypeError
for sublist in nested: #可迭代的情况
for element in flatten(sublist):
yield element
except TypeError: # 和不可迭代的情况
yield nested
print list(flatten('123')) #['123']
12345678910111213
如果表达式nested + ''引发了一个TypeError说明它不是类似字符串的序列,以被迭代;否则如果没有引发TypeError,那else子句会引发一个自己的TypeError,直接返回这个类似字符串序列。这是检查一个对象是不是类似于字符串的最简单、最快速的方法。
通用生成器
生成器由两部分组成:生成器函数和生成器的迭代器。
生成器的函数是用def
语句定义的,包含yield
的部分,
生成器的迭代器是这个函数返回的部分。
>>>def simple_geneartor():
yield 1
>>>simple_geneartor
<function simple_geneartor at 0x03003770>
>>>simple_geneartor()
<generator object simple_geneartor at 0x02FEA7D8>
12345678
生成器方法
生成器的新特性(send()
)是在开始运行后为生成器提供值的能力。
有两个注意的地方:
- 外部作用域访问该方法,需要和传递一个参数(作为值)
- 在内部挂起生成器,
yield
现在作为表达式而不是语句使用,使用send()
方法只有在生成器挂起后才有意义
>>> def repeater(value):
... while True:
... new = (yield value)
... if new is not None: value = new
...
>>> r = repeater(42)
>>> r.next()
42
>>> r.send("Hello")
'Hello'
>>>
1234567891011
生成器还有两个方法throw()
用于在yield
表达式中引发一个异常;close()
方法用于停止生成器。
9,类的浅拷贝与深拷贝
变量的赋值操作
只是形成两个变量,实际上还是指向同一个对象,内存值相同,存储相同大地址
浅拷贝
python拷贝一般都是浅拷贝,拷贝时,对象包含的子对象,内容不拷贝,因此,元对象与拷贝对象会引用同一个子对象
深拷贝
使用copy模块的deepcopy函数,递归拷贝对象中包含的子对象,源对象,和拷贝对象所有的子类也不相同
class CPU:
def __init__(self):
pass
class Disk :
def __init__(self):
pass
class Computer:
def __init__(self,cpu,disk):
self.cpu=cpu
self.disk=disk
#变量的赋值,赋值,地址相同
cpu1=CPU()
cpu2=cpu1
print(cpu1,id(cpu1))#<__main__.CPU object at 0x0000014198B98700> 1381246797568
print(cpu2,id(cpu2))#<__main__.CPU object at 0x0000014198B98700> 1381246797568
print('-------------------------')
disk=Disk()#创建一个硬盘对象
computer=Computer(cpu1,disk)#创建一个计算机对象
#浅拷贝,拷贝储大子类对象一样,但是类对象的存储地址不一样,
# 复制的本类相同,但是他的子类赋值的还是那一个,引用子类的地址
import copy
computer1=copy.copy(computer)
#<__main__.Computer object at 0x0000025C7FC9C190>
# <__main__.CPU object at 0x0000025C7FCC8700>
# <__main__.Disk object at 0x0000025C7FCC8940>
print(computer,computer.cpu,computer.disk)
#__main__.Computer object at 0x0000025C7FEC2070>
#<__main__.CPU object at 0x0000025C7FCC8700>
#<__main__.Disk object at 0x0000025C7FCC8940>
print(computer1,computer1.cpu,computer1.disk)
print('------------------------------------------')
#深拷贝,源类与子类地址值都不相同,说明重写船舰了一个新的对象,
# 还是引用了子类的框架。但是值是不一样的
computer3=copy.deepcopy(computer)
#__main__.Computer object at 0x0000026941BDC190>
# <__main__.CPU object at 0x0000026941C08700>
# <__main__.Disk object at 0x0000026941C08940>
print(computer,computer.cpu,computer.disk)
#<__main__.Computer object at 0x0000026941E0ACD0>
# <__main__.CPU object at 0x0000026941E91190>
# <__main__.Disk object at 0x0000026941E91160>
print(computer3,computer3.cpu,computer3.disk)
#创建一个计算机对象
#浅拷贝,拷贝储大子类对象一样,但是类对象的存储地址不一样,
# 复制的本类相同,但是他的子类赋值的还是那一个,引用子类的地址
import copy
computer1=copy.copy(computer)
#<__main__.Computer object at 0x0000025C7FC9C190>
# <__main__.CPU object at 0x0000025C7FCC8700>
# <__main__.Disk object at 0x0000025C7FCC8940>
print(computer,computer.cpu,computer.disk)
#__main__.Computer object at 0x0000025C7FEC2070>
#<__main__.CPU object at 0x0000025C7FCC8700>
#<__main__.Disk object at 0x0000025C7FCC8940>
print(computer1,computer1.cpu,computer1.disk)
print('------------------------------------------')
#深拷贝,源类与子类地址值都不相同,说明重写船舰了一个新的对象,
# 还是引用了子类的框架。但是值是不一样的
computer3=copy.deepcopy(computer)
#__main__.Computer object at 0x0000026941BDC190>
# <__main__.CPU object at 0x0000026941C08700>
# <__main__.Disk object at 0x0000026941C08940>
print(computer,computer.cpu,computer.disk)
#<__main__.Computer object at 0x0000026941E0ACD0>
# <__main__.CPU object at 0x0000026941E91190>
# <__main__.Disk object at 0x0000026941E91160>
print(computer3,computer3.cpu,computer3.disk)