python day10: 反射补充,面向对象

pythdon day 10

2019/10/13

学习资料来自老男孩与尚学堂

目录

1. 反射补充

反射模拟网络浏览器登录

# 初级版
'''
from lib import account url = input('please input the website address:>>> ') if url.endswith('login'):
r = account.login()
print(r)
elif url.endswith('logout'):
r = account.logout()
print(r)
else:
print('404')
''' # 中级版
'''
url = input('please input the website address:>>> ')
inp = url.split('/')[1] # ['www.baidu.com','login'][1] == 'login' if hasattr(account, inp): # 判断某个模块中是否有指定对象
target_func = getattr(account, inp, None) # 找到模块中的指定对象
r = target_func()
print(r)
else:
print('404')
''' # 高级版
# from lib import account as lat # 从lib包里面导入account模块
# print(lat.__name__)
# m = __import__('lib.account', fromlist=True) # 本质上是调用了__import__方法
# print(m.__name__)
inp = input('module/function: ').strip()
module_name, func_name = inp.split('/') # 将列表中的索引值为0与1分别赋值给前面的两个变量 m = __import__('lib.'+module_name, fromlist=True) # 因为__import__接收字符串作为参数,
if hasattr(m, func_name):
target_func = getattr(m, func_name)
r = target_func()
print(r)
else:
print('404')

16. 面向对象

16.1 面向对象初步介绍

面对对象(object oriented programming,OOP)编程的思想主要是针对大型软件设计而来的。面向对象编程命名程序的扩展性更强/可读性更好,使得编程可以像搭积木一样简单。

面向对象编程将数据和操作数据相关的方法封装到对象中,组织代码和数据的方式更加接近人的思维,从而大大提高了编程的效率。

python完全采用了面向对象的思想,是真正面向对象的编程语言,完全支持面向对象的基本功能,例如:继承/多态/封装等。

python中,一切对象。前面学习的数据类型,函数等,都是对象。

python支持面向过程/面向对象/函数式编程等多种编程范式

16.2 面向对象和面向过程区别

  • 面向过程(procedure oriented)思维

    面向过程编程更加关注的是程序的逻辑流程,是一种“执行者”思维,适合编写小规模的程序。

    面向过程思想思考问题时,我们首先思考“怎么按步骤实现?”,并将步骤对应成方法,一步一步,最终完成。这个适合简单任务,不需要过多协作的情况下。

  • 面向对象(object oriented)思维

    面向对象更加关注的是“软件中对象之间的关系”,是一种“设计者”思维,适合编写大规模的程序。(

    面向对象思想更契合人的思维模式。我们首先思考的是“怎么设计这个事物?” 。比如,思考造车,就会先思考,“车怎么设计?”,而不是“怎么按步骤造车的问题”。这就是思维方式的转变。

    面向对象方式思考造车,发现车由如下对象组成

    1. 轮胎
    2. 发动机
    3. 车壳
    4. 座椅
    5. 挡风玻璃

      为了便于协作,我们找轮胎厂完成制造轮胎的步骤,发动机厂完成制造发动机的步骤,这样,发现大家可以同时进行车的制造,最终进行组装,大大提高了效率。但是,具体到轮胎厂的一个流水线操作,仍然是有步骤的,不是离不开面向过程思想。

      因此,面向对象可以帮助我们从宏观上把握/从整体上分析整个系统。但是,具体到实现部分的微观操作(就是一个个方法),仍然需要面向过程的思路去处理

      面向对象和面向过程是相辅相成的,面向对象离不开面向过程。
  • 面向对象思考方式

    遇到复杂问题,先从问题中找名词(面向过程更多的是找动词),然后确立这些名词哪些可以作为类,再根据问题需求确定类的属性和方法,确定类之间的关系。

  • 面向对象和面向过程的总结:

  • [x] 都是解决问题的思维方式,都是代码组织的方式。

  • [x] 解决简单问题可以使用面向过程。

  • [x] 解决复杂问题:宏观上使用面向对象把握,微观处理上仍然是面向过程。

python day10: 反射补充,面向对象

16.3 对象的进化

随着编程面临的问题越来越复杂,编程语言本身也在进化,从主要处理简单数据开始,随着数据变多进化“数组”;数据类型变复杂,进化出了“结构体”;处理数据的方式和逻辑变复杂,进化出了“对象”。

  1. 简单数据:像30,40,50.2等这些数字,就是简单的数据。最初的计算机编程,都是像这样的数字。
  2. 数组:将同类型的数据放到一起。比如,整数数组[20,30,40],字符串数组['aa','bb','cc']等
  3. 结构体:将不同类型的数据放到一起,是C语言中的数据结构。比如:struct resume{int age;char name[10],double salary;};
  4. 对象:将不同类型的数据/方法(即函数)放到一起,就是对象。比如:
class Student:#旧式类写法
company = 'SXT' #类属性
conut = 0 #类属性
def __init__(self,name,score):
self.name = name #实例属性
self.score = score
Student.count = Student.count + 1
def say_score(self): #实例方法
print('我的公司是:', Student.company)
print(self.name,'我的分数是:',self.score)

17. 类class

17.1 类的定义

可以将对象比作一个“饼干”,类就是制造这个饼干的“模具”。

通过类定义数据类型的属性(数据)和方法(行为),也就是说,“类将行为和状态打包在一起”。

对象是类的具体实体,一般称为“类的实例”。类看做饼干模具,对象就是根据这个模具制造出的饼干。

从一个类创建对象时,每个对象都会共享这个类的行为(即类中定义的方法),但会有自己的属性值(不共享状态)。更具体一点:“方法代码是共享的,属性数据不共享”

python中,一切皆对象。类也称为"类对象",类的实例也称为“实例对象”。

定义类的语法格式如下:

class 类名:
类体

要点如下:

  1. 类名必须符合"标识符"的规则;一般规定,首字母大字,多个单词使用“驼峰原则”,即每个单词首字母大写。
  2. 类体中可以定性属性和方法。
  3. 属性用来描述数据,方法(即函数)用来描述这些数据相关的操作。
# 一个典型的类的定义
class Student(object):#新式类写法
def __init__(self,name,score):#__init__是构造函数
self.name = name #实例属性
self.score = score
def say_score(self): #实例方法
print(self.name,'我的分数是:',self.score) s1 = Student('张三',80)
#s1是实例对象,实际是Student.__init__(s1,'张三',80)
s1.say_score() #实际是Student.say_score(s1)

17.2 __init__构造方法和__new__方法

类是抽象的,也称为为“对象的模板”。我们需要通过类这个模板,创建类的实例对象,然后才能使用类定义的功能。

一个python对象包含三个部分:id(identity识别码)/type(对象类型)/value(对象的值)

现在,可以更进一步的说,一个python对象包含如下部分:

1. id(identity识别码)

2. type(对象类型)

3. value(对象的值)

(1) 属性(attribute)

(2) 方法(method)

创建对象,我们需要定义构造函数__init__()方法。构造方法用于执行"实例对象的初始化工作",即对象创建后,初始化当前对象的相关属性,无返回值。

init()的要点如下:

  1. 名称固定,必须为:init():
  2. 第一个参数固定,必须为self.self指的就是刚刚创建好的实例对象。
  3. 构造函数通常用来初始化实例对象的实例属性。
  4. 通过“类名(参数列表)”来调用构造函数。调用后,将创建后的对象返回给相应的变量。比如:s1 = Student('张三',80)
  5. init()方法:初始化创建好的对象,初始化指的是:给实例属性赋值
  6. new()方法:用于创建对象,但我们一般无需重定义该方法。
  7. 如果我们不定义__init__()方法,系统会提供一个默认的__init__方法。如果我们定义了带参的__init__方法,系统不创建默认的__init__方法。

注:

1. python中的self相当于C++中的self指针,JAVA和C#中的this关键字。python中,self必须为构造函数的第一个参数,名字可以任意修改。但一般遵守惯例,都叫做self。

17.3 实例属性和实例方法

17.3.1 实例属性(实例变量)

实例属性是从属于实例对象的属性,也称为“实例变量”。他的使用有如下几个要点:

  1. 实例属性一般在__init__()方法中通过如下代码定义:

    self.实例属性名 = 初始值
  2. 在本类的其他实例方法中,也是通过self 进行访问:

    self.实例属性名
  3. 创建实例对象后,通过实例对象访问:

    obj01 = 类名() #创建对象,调用__init__()初始化属性

    obj01.实例属性名 = 值 #可以给已有属性赋值,也可以新加属性

17.3.2 实例方法

实例方法是从属于实例对象的方法。实例方法的定义格式如下:

def 方法名(self [, 形参列表]):

函数体

方法的调用格式如下:

对象.方法名([实参列表])

要点:

  1. 定义实例方法时,第一个参数必须为self。和前面一样,self 指当前的实例对象。 2. 调用实例方法时,不需要也不能给self 传参。self 由解释器自动传参。
  • 函数和方法的区别
  1. 都是用来完成一个功能的语句块,本质一样。
  2. 方法调用时,通过对象来调用。方法从属于特定实例对象,普通函数没有这个特点。
  3. 直观上看,方法定义时需要传递self,函数不需要。
class Student(object):#类名一般首字母大写,多个单词每个单词首字母大写
'''定义类的测试'''
def __init__(self,name,score):#构造函数的第一个参数必须是self
self.name = name
self.score = score
def say_score(self):#对象的方法,第一个参数也必须是self。
print('{0}的分数是{1}'.format(self.name,self.score)) s1 = Student('小蓝',90) ## Student.__init(s1,'小蓝',90)
s1.say_score() ## Student.say_score(s1)
s1.age = 28
s1.salary = 3800
print(s1.salary)
print(s1.say_score)
s2 = Student('小张',20) #s2并不会有salary,age属性。
print(dir(s1)) #获得对象的的所有属性和方法
print(s1.__dict__) #对象的属性,将属性以字典形式返回
print(isinstance(s1,list)) #判断“对象”是不是“指定类型”
小蓝的分数是90
3800
<bound method Student.say_score of <__main__.Student object at 0x000001CBEBADF9B0>> ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__',
'__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__re
duce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'ag
e', 'name', 'salary', 'say_score', 'score'] {'name': '小蓝', 'score': 90, 'age': 28, 'salary': 3800} False

17.4 类对象/类属性/类方法/静态方法

17.4.1 类对象

当解释器执行class语句时,就会创建一个类对象。

class Student(object):
'''测试类之二'''
pass #空语句,占位符
print(type(Student),'\r\n*******\r\n',id(Student)) Stu2 = Student #实际就是生成了一个变量名就是类名“Student”的对象。也可以赋值给新变量Stu2.
s1 = Stu2()
print(s1)

17.4.2 类属性(类变量)

类属性是从属于"类对象"的属性,也称为"类变量"。由于类属性从属于类对象,可以被所有实例对象共享。

类属性的定义方式:

class 类名:

类变量名 = 初始值

在类中或者类的外面,我们可以通过:"类名.类变量名"来读写

class Student(object):
'''类属性的使用测试'''
company = 'SXT' #类属性
count = 0 #类属性
def __init__(self,name,score):
self.name = name #实例属性
self.score = score
Student.count += 1
def say_score(self): #实例方法
print('我的公司是:',Student.company)
print(self.name,'的分数是:',self.score)
s1 = Student('张三',80) #s1是实例对象,自动调用__init__() 方法
s1.say_score()
print('一共创建{0}个Student对象'.format(Student.count))

17.4.3 类方法

类方法是从属于"类对象"的方法。类方法通过装饰器@classmethod来定义,格式如下:

@classmethod

def 类方法名(cls [,开参列表]):

函数体

要点如下:

  1. @classmethod必须位于方法上面一行。
  2. 第一个cls必须有;cls指的就是"类对象"本身。
  3. 调用类方法格式:"类名.类方法名(参数列表)"。参数列表中,不需要也不能给cls传递值。
  4. 类方法中访问实例属性和实例方法导致错误。
  5. 子类继承父类方法时,传入cls是子类对象,而非父类对象
class Student(object):
'''类属性/类方法的使用测试'''
company = 'SXT' #类属性
count = 0 #类属性
@classmethod
def printCompany(cls):
print(cls.company)
def __init__(self,name,score):
self.name = name #实例属性
self.score = score
Student.count += 1
def say_score(self): #实例方法
print('我的公司是:',Student.company)
print(self.name,'的分数是:',self.score)
# s1 = Student('张三',80) #s1是实例对象,自动调用__init__() 方法
# s1.say_score()
# print('一共创建{0}个Student对象'.format(Student.count))
Student.printCompany()

17.4.4 静态方法

python中允许定义与"类对象"无关的方法,称为"静态方法".

“静态方法”和在模块中定义普通函数没有区别,只不过“静态方法”放到了“类的名字空间里面”,需要通过“类调用”。

静态方法通过装饰器@staticmethod来定义,格式如下:

    @staticmethod
def 静态方法名([形参列表]) :
函数体

要点如下:

  1. @staticmethod必须位于方法上面一行
  2. 调用静态方法格式:“类名.静态方法名(参数列表)”。
  3. 静态方法中访问实例属性和实例方法会导致错误
class Student(object):
'''类属性的使用测试'''
company = 'SXT' #类属性
count = 0 #类属性 def __init__(self,name,score):
self.name = name #实例属性
self.score = score
Student.count += 1
def say_score(self): #实例方法
print('我的公司是:',Student.company)
print(self.name,'的分数是:',self.score) @staticmethod
def add(a, b): # 静态方法
print('{0}+{1}={2}'.format(a, b, a + b))
return a + b @classmethod
def printCompany(cls):#类方法
print(cls.company) s1 = Student('张三',80) #s1是实例对象,自动调用__init__() 方法
# s1.say_score()
# print('一共创建{0}个Student对象'.format(Student.count))
# Student.printCompany()
print(Student.add(1.4,7))
s1.add(1,2) 1.4+7=8.4
8.4
1+2=3

17.5 del()方法(析构函数)和垃圾回收机制

__del__方法称为“析构方法”,用于实现对象被销毁时所需的操作。比如:释放对象 占用的资源,例如:打开的文件资源、网络连接等。

Python实现自动的垃圾回收,当对象没有被引用时(引用计数为 0),由垃圾回收器调用__del__方法。

我们也可以通过del 语句删除对象,从而保证调用__del__方法。

系统会自动提供__del__方法,一般不需要自定义析构方法。

class Person(object):
def __del__(self):
print('销毁对象{0}'.format(self)) p1 = Person()
p2 = Person()
del p2
print('程序结束 ')

17.6 call()方法和可调用对象

定义了__call__方法的对象,称为“可调用对象”,即该对象可以像函数一样被调用

class SalaryAccount(object):
def __call__(self, salary,*args, **kwargs):
print('算工资啦')
# return 3000
yearsalary = salary*12
daysalary = salary//22.5
hoursalary = daysalary//8 return dict(yearsalary= yearsalary,daysalary=daysalary,hoursalary=hoursalary) s = SalaryAccount()
print(s(30000))

17.7 方法没有重载/方法的动态性

17.7.1 方法没有重载

在其他语言中,可以定义多个重名的方法,只要保证方法签名唯一即可。方法签名包含 3 个部分:方法名、参数数量、参数类型

Python中,方法的的参数没有声明类型(调用时确定参数的类型),参数的数量也可以由 可变参数控制。因此,Python中是没有方法的重载的。定义一个方法即可有多种调用方式, 相当于实现了其他语言中的方法的重载。

如果我们在类体中定义了多个重名的方法,只有最后一个方法有效。

建议:不要使用重名的方法!Python中方法没有重载。只有最后一个有效。

17.7.2 方法的动态性

class Person(object):
# def __del__(self):
# print('销毁对象{0}'.format(self))
def work(self):
print('努力上班!') def play_game(self):
print('{0}玩游戏'.format(self)) def work2(s):
print('好好工作,努力上班') Person.play_game = play_game p1 = Person()
p2 = Person()
p1.work()
p1.play_game() Person.work = work2 p1.work() 努力上班!
<__main__.Person object at 0x000001D55025F9B0>玩游戏
好好工作,努力上班

17.8 私有属性和私有方法(实现封装)

Python对于类的成员没有严格的访问控制限制,这与其他面向对象语言有区别。关于私有 属性和私有方法,有如下要点:

  1. 通常我们约定,两个下划线开头的属性是私有的(private)。其他为公共的(public)
  2. 类内部可以访问私有属性(方法)
  3. 类外部不能直接访问私有属性(方法)
  4. 类外部可以通过“_类名__私有属性(方法)名”访问私有属性(方法)

【注】方法本质上也是属性!只不过是可以通过()执行而已。所以,此处讲的私有属性和公 有属性,也同时讲解了私有方法和公有方法的用法。如下测试中,同时也包含了私有方法和 公有方法的例子。

#测试私有属性
class Employee(object):
__company = '蓝星科技' #类的私有属性
def __init__(self,name,age):
self.name = name
self.__age = age #实例的私有属性,加了两个下划线
def __work(self):#私有方法,加了两个下划线
print('好好工作,赚钱养老婆')
print('年龄:{0}'.format(self.__age))#类内部调用私有属性是完全没有问题的。
print(Employee.__company) e = Employee('小蓝',18)
print(e.name)
# print(e.__age)
print(e._Employee__age)
print(dir(e))
e._Employee__work()
print(Employee._Employee__company) 小蓝
18
['_Employee__age', '_Employee__company', '_Employee__work', '__class__', '__delattr__', '__dict__', '__dir__', '__do
c__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '
__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__s
izeof__', '__str__', '__subclasshook__', '__weakref__', 'name']
好好工作,赚钱养老婆
年龄:18
蓝星科技
蓝星科技

从打印的 Person 对象所有属性我们可以看出。私有属性“__age”在实际存储时是按照 “_Person__age”这个属性来存储的。这也就是为什么我们不能直接使用“__age”而可以 使用“_Person__age”的根本原因。

17.9 @property装饰器

@property可以将一个方法的调用方式变成"属性调用"。一般用来给对应的属性增加get和set方法。

@property 主要用于帮助我们处理属性的读操作、写操作。对于某一个属性,我们可以直 接通过:

emp1.salary = 30000

如上的操作:读操作、写操作。但是,这种做法不安全。比如,我需要限制薪水必须为1-10000 的数字。这时候,我们就需要通过getter、setter方法来处理。

class Employee(object):
def __init__(self,name,salary):
self.__name = name
self.__salary = salary @property #定义下面的方法变成了一个属性
def salary(self):
print('计算工资')
return self.__salary @salary.getter
def get_salary(self,salary):
return self.__salary @salary.setter #针对salary属性的一个设置
def salary(self,salary):
if 1000<salary<50000:
self.__salary = salary
else:
print('录入错误,薪水在1000--50000这个范围')
'''
def get_salary(self):
return self.__salary def set_salary(self,salary):
if 1000<salary<50000:
self.__salary = salary
else:
print('录入错误,薪水在1000--50000这个范围')
''' # emp1 = Employee('蓝星',30000)
# print(emp1.get_salary())
# emp1.set_salary(20000)
# print(emp1.get_salary())
# emp1.salary = 20000 #不能设置
# emp1.salary() #也可像方法一样调用了,因为salary已经是一个属性了。 emp2 = Employee('lanxing',20000)
print(emp2.salary) emp2.salary = -29990
emp2.salary = 2000

17.10 属性和方法命名总结/类编程风格

17.10.1 属性和方法命名总结

· _xxx:保护成员,不能用“frommodule import * ”导入,只有类对象和子类对象能访 问这些成员。

· __xxx__:系统定义的特殊成员

· __xxx: 类中的私有成员,只有类对象自己能访问,子类对象也不能访问。(但,在类外部可以通过“对象名._类名__xxx”这种特殊方式访问。 Python 不存在严格意义的私有成员)

注:再次强调,方法和属性都遵循上面的规则。

17.10.2 类编码风格

  1. 类名首字母大写,多个单词之间采用驼峰原则。
  2. 实例名、模块名采用小写,多个单词之间采用下划线隔开。
  3. 每个类,应紧跟“文档字符串”,说明这个类的作用
  4. 可以用空行组织代码,但不能滥用。在类中,使用一个空行隔开方法;模块中,使用两个空行隔开多个类

17.11 继承(inheritance)

继承是面向对象程序设计的重要特征,也是实现“代码复用”的重要手段。

如果一个新类继承自一个设计好的类,就直接具备了已有类的特征,就大大降低了工作难度。已有的类,我们称为“父类或者基类”,新的类,我们称为“子类或者派生类”

17.11.1 继承的语法格式

python支持多重继承,一个子类可以继承多个父类。继承的语法格式如下:

class 子类类名(父类1[,父类2,...]):

类体

如果在类定义中没有指定父类,则默认父类是object类。也就是说,object是所有类的父类,里面定义了一些所有类共有的默认实现,比如__new__().

定义子类时,必须在其构造函数中调用父类的构造函数。调用格式如下:

父类名.init(self,参数列表):

class Person(object):

    def __init__(self,name,age):
self.name = name
self.__age = age #私有属性 def say_age(self):
print('年龄,年龄,我也不知道') class Student(Person): def __init__(self,name,age,score):
#调用父类的构造函数方法一
Person.__init__(self,name,age)#语法级别上不调用也没错,但是作为子类,必须要去调用,不然这个子类就没有name属性了。
self.score = score #Student--->Person--->object类
print(Student.mro())#打印继承顺序 s = Student('lanxing',22,78)
s.say_age()
print(s.name)
# print(s.age)
print(s._Person__age)

17.11.2 类成员的继承和重写

  1. 成员继承:子类继承了父类除构造方法之外的所有成员。
  2. 方法重写:子类可以重新定义父类中的方法,这样就会覆盖父类的方法,也称为重写
#类成员继承和方法重写的案例

class Person(object):

    def __init__(self,name,age):
self.name = name
self.__age = age def say_age(self):
print('我的年龄是',self.__age) def say_intro(self):
print('我的名字是{0}'.format(self.name)) class Student(Person): def __init__(self,name,age,score):
Person.__init__(self,name,age) #必须显式的调用父类初始化方法,不然解释器不会去调用。
self.score = score def say_score(self):
print(self.name,'的分数是:',self.score ) def say_name(self): #重写父类的方法
print('报告老师,我是',self.name) s1 = Student('张三',15,85) s1.say_score()
s1.say_name()
s1.say_age()

17.11.3 查看类的继承层次结构

通过类的方法mro()或者类的属性__mro__可以输出这个类的继承层次结构。

class A:
pass
class B(A):
pass
class C(B):
pass #print(C.mro())
print(C.__mro__)#与上方代码是一样的效果 [<class '__main__.C'>, <class '__main__.B'>, <class '__main__.A'>, <class 'object'>]

17.12 object根类

17.12.1 object根类的属性

object类是所有类的父类,因此所有的类都有object类的属性和方法。显然有必要深入研究下object类的结构。

  • dir()查看对象属性

    内置函数dir()可以查看指定对象所有的属性。
class Person(object):

    def __init__(self,name,age):
self.name = name
self.__age = age #私有属性 def say_age(self):
print(self.name,'的年龄是:',self.age) obj = object() #obj是object这个基类实例化的对象
print(dir(obj)) #输出obj这个对象的所有属性
print('----------')
s2 = Person('lanxing',19)
print(dir(s2)) ['__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__'] ------------------- ['_Person__age', '__class__', '__delattr__', '__dict__', '__dir__', '_
_doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__
', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '_
_module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__rep
r__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__w
eakref__', 'name', 'say_age']

从上面我们可以发现这样几个要点:

  1. Person对象增加了六个属性: dict, module, weakref ,age, name, say_age
  2. object的所有属性,Person 类作为object 的子类,显然包含了所有的属性。
  3. 我们打印age、name、say_age,发现say_age 虽然是方法,实际上也是属性。只不过, 这个属性的类型是“method”而已。
    age <class 'int'>
name <class 'str'>
say_age <class 'method'>

17.12.2 重写__str__方法

class Person(object):

    def __init__(self,name,age):
self.name = name
self.__age = age #私有属性 p = Person('LANXING',22)
print(p) <__main__.Person object at 0x000001EC5F1CF940> class Person(object): def __init__(self,name,age):
self.name = name
self.__age = age #私有属性 def __str__(self):
'''将对象转化成一个字符串,一般用于print方法'''
return '名字是:{0},年龄是{1}'.format(self.name,self.__age) p = Person('LANXING',22)
print(p) 名字是:LANXING,年龄是22

17.13 多重继承

python支持多重继承,一个子类可以有多个"直接父类"。这样,就具备了多个父类的特点。但是,由于这样会被类的整体层次搞的异常复杂,尽量避免使用

在python3中,不管是新式类写法还是经典类写法,都是按照广度优先进行查询。

python2中,新式类写法是按照广度优先,经典类写法是按照深度优先。

class A:    #经典类写法
pass
class B(A): #新式类写法
pass
class C(B,A): #多重继承
pass
class D(C,B)
#广度优先就是D先从C查询,C没有,就找B,B再没有就找A。
#深度优先就是D先从C查询,如果C没有,就再找A。

17.14 super()方法获得父类定义

在子类中,如果想要获得父类的方法时,可以通过super()来获得。

super()代表父类的定义,不是父类的对象。

class A:

    def say(self):
print('A:',self)
class B(A): def say(self):
# A.say(self)
super().say() #super()=A
print('B:',self) B().say()

17.15 多态(polymorphism)

多态(polymorphism)是指同一个方法调用由于对象不同可能会产生不同的行为。

关于多态要注意以下2点:

  1. 多态是方法的多态,属性没有多态。
  2. 多态的存在有2个必要条件:继承和方法重写。
class Man(object):

    def eat(self):
print('饿了,吃饭了!') class Chinese(Man): def eat(self): #方法重写
print('中国人用筷子吃饭') class English(Man): def eat(self):
print('英国人用叉子吃饭') class Indian(Man): def eat(self):
print('印度人用右手吃饭') def manEat(m):
if isinstance(m,Man):
m.eat() #多态
else:
print('不能吃饭') manEat(Chinese())
manEat(English())

17.16 特殊方法和特性属性

python day10: 反射补充,面向对象

17.17 对象的浅拷贝和深拷贝

  • 变量的赋值操作

    只是形成两个变量,实际还是指向同一个对象。
  • 浅拷贝

    Python拷贝一般都是浅拷贝。拷贝时,对象包含的子对象内容不拷贝。因此,源对象 和拷贝对象会引用同一个子对象。
  • 深拷贝

    使用copy模块的 deepcopy 函数,递归拷贝对象中包含的子对象。源对象和拷贝对象所有的子对象也不同。
import copy

class   MobilePhone(object):

    def __init__(self,cpu,screen):
self.cpu = cpu
self.screen = screen class Cpu:
def calculate(self):
print('计算,算个12345')
print('cpu的对象',self) class Screen:
def show(self):
print('显示一个好看的画面')
print('屏幕对象:',self) #测试变量赋值
c1 = Cpu()
c2 = c1
# print(c1)
# print(c2) #测试浅复制
s1 = Screen()
m1 = MobilePhone(c1,s1)
m2 = copy.copy(m1) # print(m1,m1.cpu,m1.screen)
# print(m2,m2.cpu,m2.screen) #测试深复制 m3 = copy.deepcopy(m1)
print(m1,m1.cpu,m1.screen)
print(m3,m3.cpu,m3.screen)

17.18 组合

"is-a"关系,我们可以使用“继承”。从而实现子类拥有父类的方法和属性。“is-a” 关系指的是类似这样的关系:狗是动物,dog is animal。狗类就应该继承动物类。

"has -a"关系,我们可以使用“组合”,也能实现一个类拥有另一个类的方法和属性。has-a”关系指的是这样的关系:手机拥有 CPU。 MobilePhone has a CPU.

#使用继承实现代码的复用
class A1: def say_a1(self):
print('a1,a1,a1') class B1(A1):
pass b1 = B1()
b1.say_a1() #同样的效果,使用组合来实现代码的复用
class A2: def say_a2(self):
print('a2,a2,a2') class B2: def __init__(self,a):
self.a =a a2 = A2()
b2 = B2(a2)
b2.a.say_a2()

18. 面向对象三大特征

python是面向对象的语言,也支持面向对象编程的三大特性:继承/封装(隐藏)/多态

18.1 封装(隐藏)

隐藏对象的属性和实现细节,只对外提供必要的方法。相当于将“细节封装起来”,只对外暴露“相关调用的方法”。

通过前面学习的“私有属性/私有方法”的方式,实现“封装”。python追求简洁的语法,没有严格的语法级别的访问控制符,更多的是依靠程序员自觉实现。

18.2 继承

继承可以让子类具有父类的特性,提高了代码的重用性

从设计上是一种增量进化,原有父类设计不变的情况下,可以增加新的功能,或者改进已有的算法。

18.3 多态

多态是指同一个方法调用由于对象不同会产生不同的行为。生活中这样的例子比比皆是:同样是休息方法,人不同休息方法不同。张三休息是睡觉,李四休息是玩游戏,程序员休息是"敲几行代码"。即“一个接口,多种实现”。

19. 设计模式:工厂模式与单例模式

设计模式是面向对象语言特有的内容,是我们在面临某一类问题时候固定的做法,设计模式有很多种,比较流行的是:GOF,23种设计模式。

对于初学者,学习两个最常用的模式:工厂模式与单例模式。

19.1 工厂模式

工厂模式实现了创建者和调用者的分离,使用专门的工厂类将选择实现类/创建对象进行了统一的管理和控制。越是大型的软件,越是需要工厂模式。

#工厂模式

class CarFactory(object):

    def createCar(self,brand):
if brand =="benz":
return Benz()
elif brand =="baoma":
return Baoma()
elif brand == "biyadi":
return Biyadi()
else:
return '未知品牌,不能代工'
class Benz:
pass
class Baoma:
pass
class Biyadi:
pass factory = CarFactory()
c1 = factory.createCar('benz')
c2 = factory.createCar('baoma')
print(c1)
print(c2) <__main__.Benz object at 0x0000022FD5D604A8>
<__main__.Baoma object at 0x0000022FD5D604E0>

19.2 单例模式

单例模式(singleton pattern)的核心作用是确保一个类只有一个实例对象,并且提供一个访问该实例的全局访问点

单例模式只生成一个实例对象,减少了对系统资源的开销。当一个对象的产生需要比较多的资源,如读取配置文件/产生其他依赖对象时,可以产生一个“单例对象”,然后永久驻留内存中,从而极大的降低开销。

单例模式有多种实现的方法,这里推荐重写__new__()的方法。

class MySingleton(object):

    __obj = None    #类属性
__init_flag = True def __new__(cls, *args, **kwargs):
if cls.__obj == None:
cls.__obj =object.__new__(cls)
return cls.__obj def __init__(self,name):
if MySingleton.__init_flag:
self.name = name
print('init.....')
MySingleton.__init_flag = False a = MySingleton('aa')
b = MySingleton('bb')
print(a)
print(b)
上一篇:python ---16 初识面向对象


下一篇:python:代码复用与函数递归