面向对象编程
对象(object):表示客观世界问题空间中的某个具体事物,又表示软件系统解空间的中的基本元素
面向对象程序设计(Object-oriented programing, OP):是一种程序设计范型,也是一种程序开发的方法。
1、类
类(clas)是一种面向对象计算机编程语言的构造,是创建对象的蓝图,描述了所创建的对象共同的属性和方法
1.理解创建类的基本方法
2.初步理解对象和面向对象
class SuperMan:
'''
A class of superman
'''
def __init__(self,name):
self.name = name
self.gender = 1
self.single = False
self.illness = False
def nine_negative_kungfu(self):
return "Ya!You have to die."
guojing = SuperMan('guojing')
print(guojing.name)
print(guojing.gender)
kongfu = guojing.nine_negative_kungfu()
print(kongfu)
'''
编写一个程序,用于判断学生是否已经完成作业。如果完成,教
师会给出表扬,否则要进行批评。
'''
class Student:
def __init__(self, name, grade, subject):
self.name = name
self.grade = grade
self.subject = subject
def do_work(self, time):
if self.grade > 3 and time > 2:
return True
elif self.grade < 3 and time > 0.5:
return True
else:
return False
class Teacher:
def __init__(self, name, subject):
self.name = name
self.subject = subject
def evaluate(self, result=True):
if result:
return "You are great."
else:
return "You should work hard"
stu_zhang = Student('zhang', 5, 'math')
tea_wang = Teacher('wang', 'math')
teacher_said = tea_wang.evaluate(stu_zhang.do_work(1))
print("Teacher {0} said: {1}, {2}".format(tea_wang.name, stu_zhang.name, teacher_said))
stu_newton = Student('Newton', 6, 'physics')
teacher_newton = tea_wang.evaluate(stu_newton.do_work(4))
print("Teacher {0} said: {1}, {2}".format(tea_wang.name, stu_newton.name, teacher_newton))
1.1、属性
属性:描述对象是什么
• 类属性
又称静态属性
只有通过类才能修改
实例也拥有类属性,但不能修改类属性
>>> class Foo:
lang = 'python'
>>> dir(Foo)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'lang']
>>> Foo.lang
'python'
>>> f = Foo()
>>> type(f)
<class '__main__.Foo'>
>>> dir(f)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'lang']
>>> f.lang
'python'
>>> Foo.group = 'winner classroom'
>>> dir(Foo)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'group', 'lang']
>>> dir(f)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'group', 'lang']
>>> f.name = 'winner'
>>> dir(f)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'group', 'lang', 'name']
>>> dir(Foo)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'group', 'lang']
• 实例属性
实例属性,又称动态属性
通过实例创建
不同实例的实例属性不同
实例的__dict__显示当前实例的所有属性
>>> class Bar:
def __init__(self,name):
self.name = name
>>> b = Bar('winner')
>>> c = Bar('xiaochun')
>>> b.name
'winner'
>>> c.name
'xiaochun'python
>>> b.__dict__
{'name': 'winner'}
>>> class Foo:
lang = 'python'
def __init__(self,name):
self.name = name
>>> f = Foo('winner')
>>> dir(f)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'lang', 'name']
>>> f.__dict__
{'name': 'winner'}
>>> f.lang #lang是类属性,而不是实例属性,不能在__dict__中列出,但是它可以被实例访问
'python'
• self的作用
类中的方法,如无特别规定,都是以self作为第一参数
self引用当前实例
>>> class Bar:
def __init__(self):
print(self)
>>> b = Bar()
<__main__.Bar object at 0x00000181FD3E1580>
>>> b
<__main__.Bar object at 0x00000181FD3E1580>
>>>
'''
创建类,能够计算任意两个日期之间的天数、周数。
'''
import datetime
from dateutil import rrule
class BetDate:
def __init__(self, start_date, stop_date):
self.start = datetime.datetime.strptime(start_date, "%Y, %m, %d")
self.stop = datetime.datetime.strptime(stop_date, "%Y, %m, %d")
def days(self):
d = self.stop - self.start
return d.days if d.days > 0 else False
def weeks(self):
weeks = rrule.rrule(rrule.WEEKLY, dtstart=self.start, until=self.stop)
return weeks.count()
fir_twe = BetDate("2019, 5, 1", "2019, 11, 25")
d = fir_twe.days()
w = fir_twe.weeks()
print("Between 2019-5-1, 2019-11-25:")
print("Days is:", d)
print("Weeks is:", w)
1.2、方法
方法:描述对象能做什么
• 比较方法和函数
名称的命名、代码块的编写方式都一样
(实例)方法不能单独调用,只能通过实例/类调用,而函数可以单独使用
方法的第一个参数必须是self
>>> class Foo:
def method(self,x):
return x * 2
>>> f = Foo()
>>> f.method(2)
4
>>> Foo.method(2)
Traceback (most recent call last):
File "<pyshell#320>", line 1, in <module>
Foo.method(2)
TypeError: method() missing 1 required positional argument: 'x'
>>> Foo.method(f,2)
4
>>> f.method #类是对象,方法也是对象
<bound method Foo.method of <__main__.Foo object at 0x00000181FD3E1820>>
• 类方法
使用装饰器:@classmethod装饰符
类方法的第一个参数:cls,表示类本身,和写成什么没关系,习惯于使用cls
>>> class Bar:
@classmethod
def method(cls,x):
print(cls)
>>> b = Bar()
>>> b.method(2)
<class '__main__.Bar'>
>>> Bar
<class '__main__.Bar'>
• 静态方法
使用装饰器:@staticmethod装饰符
静态方法不与实例绑定
>>> class Bar2:
@staticmethod
def add():
return 'add'
>>> b2 = Bar2()
>>> b2.add()
'add'
>>> Bar2.add()
'add'
>>> add()
Traceback (most recent call last):
File "<pyshell#343>", line 1, in <module>
add()
TypeError: add() missing 2 required positional arguments: 'x' and 'y'
练习
'''
创建类,能够通过“年-月-日”字符串创建实例,并检验年、月、日是否合法
'''
class Date(object):
def __init__(self,year=0,month=0,day=0):
self.year = year
self.month = month
self.day = day
@classmethod
def from_string(cls,date_as_string):
year,month,day = map(int,date_as_string.split('-'))
datel = cls(year,month,day)
return datel
@staticmethod
def is_date_valid(date_as_string):
year, month, day = map(int, date_as_string.split('-'))
return day <= 31 and month<=12 and year <= 2038
d = Date.from_string('2019-11-11')
is_date = Date.is_date_valid('2019-11-11')
print(is_date)
2、继承
继承是对象的特性之一
继承是面向对象编程的一个重要概念
• 父类和子类
对父类方法的重写:如果在子类中写了一个与父类同名的方法,会覆盖父类中的方法
>>> class P:
def __init__(self,name):
self.name = name
def eat(self):
return "fish"
>>> class C(P):
pass
>>> c = C()
Traceback (most recent call last):
File "<pyshell#356>", line 1, in <module>
c = C()
TypeError: __init__() missing 1 required positional argument: 'name'
>>> c = C('google')
>>> dir(c)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'eat', 'name']
>>> class D(P):pass
>>> dir(D)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'eat']
>>> class E(P):
def __init__(self,age):
self.age = age
>>> e = E('winner',18)
Traceback (most recent call last):
File "<pyshell#367>", line 1, in <module>
e = E('winner',18)
TypeError: __init__() takes 2 positional arguments but 3 were given
>>> e = E('winner')
>>> dir(e)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'age', 'eat']
>>> e.age
'winner'
>>> e.name
Traceback (most recent call last):
File "<pyshell#371>", line 1, in <module>
e.name
AttributeError: 'E' object has no attribute 'name'
在单继承中,如果遇到子类覆盖了父类中的同名方法,又想在子类中使用父类的方法,子类中调用父类中的方法
>>> class E(P):
def __init__(self,name,age):
self.age = age
P.__init__(self,name)
>>> e = E('winner',18)
>>> e.age
18
>>> e.name
'winner'
>>> class E(P):
def __init__(self,name,age):
self.age = age
super().__init__(name)
>>> e = E('winner',18)
>>> e.age
18
>>> e.name
'winner'
• 单继承
class Person:
def __init__(self,name,age):
self.name = name
self.age = age
def get_name(self):
return self.name
def get_age(self):
return self.age
class Student(Person):
def __init__(self,school,name,age):
self.school = school
super().__init__(name,age)
def grade(self,n):
print("{0}'s grade is {1}".format(self.name,str(n)))
stu1 = Student('Soochow','winner','18')
stu1.grade(99)
print(stu1.get_name())
print(stu1.get_age())
• 多继承
class K1:
def foo(self):
print("K1-foo")
class K2:
def foo(self):
print("K2-foo")
def bar(self):
print("K2-bar")
class J1(K1,K2):
pass
class J2(K1,K2):
def bar(self):
print("J2-bar")
class C(J1,J2):
pass
print(C.__mro__)
m = C()
m.foo()
m.bar()
运行结果
(<class '__main__.C'>, <class '__main__.J1'>, <class '__main__.J2'>, <class '__main__.K1'>, <class '__main__.K2'>, <class 'object'>)
K1-foo
J2-bar
'''
请编写“物理学家”的类,并将其作为“理论物理学家”
和“实验物理学家”两个类的父类
'''
class Physicist:
def __init__(self,name,iq=120,looks='handsom',subject='physics'):
self.name = name
self.iq = iq
self.looks = looks
self.subject = subject
def research(self,field):
print("{0} research {1}".format(self.name,field))
def speak(self):
print("My name is",self.name)
print("I am",self.looks)
print("Intelligence is",self.iq)
print("I like",self.subject)
class ExperimentalPhysicist(Physicist):
def __init__(self,main_study,name,iq=120,looks='handsom',subject='physics'):
self.main_study = main_study
super().__init__(name,iq,looks,subject)
def experiment(self):
print("{0} is in Physics Lab".format(self.name))
class TheoreticalPhysicist(Physicist):
def __init__(self,theory,name,iq=120,looks='handsom',subject='physics'):
self.theory = theory
super().__init__(name,iq,looks,subject)
def research(self,field,base):
super().research(field)
print("My theory is {0}, it is based on {1}".format(self.theory,base))
3、多态
- 多态是对象的特性之一
- Python语言天然具有多态性
>>> def add(x,y):
return x + y
>>> add(3,4)
7
>>> add('winner','python')
'winnerpython'
4、封装
- 封装是对象的特性之一
- Python中通过私有化的方式实现封装,在对象前面加上两个下划线__
>>> class Foo:
__name = 'winner'
book = 'python'
>>> Foo.book
'python'
>>> Foo.__name
Traceback (most recent call last):
File "<pyshell#407>", line 1, in <module>
Foo.__name
AttributeError: type object 'Foo' has no attribute '__name'
>>> Foo.name
Traceback (most recent call last):
File "<pyshell#408>", line 1, in <module>
Foo.name
AttributeError: type object 'Foo' has no attribute 'name'
>>> f = Foo()
>>> f.book
'python'
>>> f.name
Traceback (most recent call last):
File "<pyshell#411>", line 1, in <module>
f.name
AttributeError: 'Foo' object has no attribute 'name'
>>> f.__name
Traceback (most recent call last):
File "<pyshell#412>", line 1, in <module>
f.__name
AttributeError: 'Foo' object has no attribute '__name'
被封装的对象只能在该类中被调用
>>> class Foo:
def __init__(self):
self.name = 'winner'
self.__group = 'winner classroom'
def code(self):
return self.__group
def __author(self):
return self.name
>>> f = Foo()
>>> f.code()
'winner classroom'
>>> f.__group
Traceback (most recent call last):
File "<pyshell#426>", line 1, in <module>
f.__group
AttributeError: 'Foo' object has no attribute '__group'
>>> f.__author()
Traceback (most recent call last):
File "<pyshell#427>", line 1, in <module>
f.__author()
AttributeError: 'Foo' object has no attribute '__author'
5、定制类
- 理解类和类型
- 掌握自定义对象类型的方法
class RoundFloat:
def __init__(self, val):
self.value = round(val, 2)
def __str__(self): # str:用户友好;repr: 解释器友好
return "{0:.2f}".format(self.value)
__repr__ = __str__
r = RoundFloat(3.1415926)
print(r)
print(type(r))
>>> class Foo:
def __repr__(self):
return "I am in repr"
def __str__(self):
return "I am in str"
>>> f = Foo()
>>> s
['one', 'two', 'three', 'four']
>>> f
I am in repr
>>> print(f)
I am in str
>>>
class Fraction:
def __init__(self, number, denom=1):
self.number = number
self.denom = denom
def __str__(self):
return str(self.number) + "/" + str(self.denom)
__repr__ = __str__
f = Fraction(2, 3)
print(f)
>>> from fractions import Fraction #分数
>>> m,n = Fraction(1,6),Fraction(3,6)
>>> m
Fraction(1, 6)
>>> n
Fraction(1, 2)
>>> m+n
Fraction(2, 3)
6、控制属性访问
6.1优化内存:slots
>>> a = Foo()
>>> a.__dict__
{}
>>> a.age = 25
>>> a.__dict__
{'age': 25}
>>> a.year = 28
>>> a.__dict__
{'age': 25, 'year': 28}
对类的属性进行控制,不允许随便增减,只允许指定属性,而且不允许修改(只读)
>>> class Bar:
__slots__ = ('name','age')
>>> Bar.name='winner'
>>> Bar.age=28
>>> b=Bar()
>>> b.name
'winner'
>>> b.age
28
>>> b.city = "soochow"
Traceback (most recent call last):
File "<pyshell#476>", line 1, in <module>
b.city = "soochow"
AttributeError: 'Bar' object has no attribute 'city'
>>> b.age = 38
Traceback (most recent call last):
File "<pyshell#477>", line 1, in <module>
b.age = 38
AttributeError: 'Bar' object attribute 'age' is read-only
6.2掌握与属性相关的特殊方法__getattr__和__setattr__
访问类中不存在的属性时
>>> class A:
def __getattr__(self,name):
print("you use getattr")
def __setattr__(self,name,value):
print("you use setattr")
self.__dict__[name] = value
>>> a = A()
>>> a.x
you use getattr
>>> a.x = "haha"
you use setattr
>>> a.x
'haha'
7、迭代器和生成器
判断一个对象是否时可迭代对象
>>> hasattr(list,"__iter__")
True
7.1、迭代器
列表时可迭代的(非迭代器对象),但__next__是迭代器对象特有的
>>> hasattr(iter_lst,"__next__")
True
>>> hasattr(list,"__next__")
False
将迭代器中的元素读到内存中
>>> lst = [1,2,3,4]
>>> iter_lst = iter(lst)
>>> iter_lst
<list_iterator object at 0x00000181FD3F7790>
>>> lst
[1, 2, 3, 4]
>>> hasattr(iter_list,"__iter__")
Traceback (most recent call last):
File "<pyshell#501>", line 1, in <module>
hasattr(iter_list,"__iter__")
NameError: name 'iter_list' is not defined
>>> hasattr(iter_lst,"__iter__")
True
>>> hasattr(iter_lst,"__next__")
True
>>> iter_lst.__next__()
1
>>> iter_lst.__next__()
2
>>> iter_lst.__next__()
3
>>> iter_lst.__next__()
4
>>> iter_lst.__next__()
Traceback (most recent call last):
File "<pyshell#509>", line 1, in <module>
iter_lst.__next__()
StopIteration
>>> for i in iter_lst:
print(i)
>>> iter_lst =iter(lst)
>>> for i in iter_lst:print(i)
1
2
3
4
class MyRange:
def __init__(self, n):
self.i = 1
self.n = n
def __iter__(self):
return self
def __next__(self):
if self.i <= self.n:
i = self.i
self.i += 1
return i
else:
raise StopIteration()
print("range(7):", list(range(7)))
print("MyRange(7):", [i for i in MyRange(7)])
class Fibs:
def __init__(self, max):
self.max = max
self.a = 0
self.b = 1
def __iter__(self):
return self
def __next__(self):
fib = self.a
if fib > self.max:
raise StopIteration
self.a, self.b = self.b, self.a + self.b
return fib
fibs = Fibs(1000000)
lst = [fibs.__next__() for i in range(10)]
print(lst)
>>> import itertools
>>> counter = itertools.count(start=7)
>>> next(counter)
7
>>> next(counter)
8
>>> next(counter)
9
7.2、生成器
>>> def g():
yield 0
yield 1
yield 2
>>> ge = g()
>>> ge
<generator object g at 0x00000181FD3EDCF0>
>>> type(ge)
<class 'generator'>
>>> dir(ge)
['__class__', '__del__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__lt__', '__name__', '__ne__', '__new__', '__next__', '__qualname__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'close', 'gi_code', 'gi_frame', 'gi_running', 'gi_yieldfrom', 'send', 'throw']
>>>
yield和return
执行到yield时,函数被挂起,等待下一次执行next
执行到return时,函数终止
>>> def r_return(n):
print("start")
while n > 0:
print("before return")
return n
n -= 1
print('after return')
>>> rre = r_return(3)
start
before return
>>> print(rre)
3
>>> def y_yield(n):
print("start")
while n > 0:
print("before yield")
yield n
n -= 1
print('after yield')
>>> yy = y_yield(3)
>>> yy.__next__()
start
before yield
3
>>> yy.__next__()
after yield
before yield
2
>>> yy.__next__()
after yield
before yield
1
>>> yy.__next__()
after yield
Traceback (most recent call last):
File "<pyshell#561>", line 1, in <module>
yy.__next__()
StopIteration
def fibs():
prev, curr = 0, 1
while True:
yield prev
prev, curr = curr, prev + curr
import itertools
print(list(itertools.islice(fibs(), 10)))
生成器解析
>>> [x**2 for x in range(10)]
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
>>> gt = (x**2 for x in range(10))
>>> gt
<generator object <genexpr> at 0x00000181FD3EDE40>
>>> gt.__next__()
0
>>> gt.__next__()
1
>>> gt.__next__()
4
>>> gt.__next__()
9