博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

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

Posted on 2019-10-13 23:20  bluestarpin  阅读(180)  评论(0编辑  收藏  举报

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. 挡风玻璃
      为了便于协作,我们找轮胎厂完成制造轮胎的步骤,发动机厂完成制造发动机的步骤,这样,发现大家可以同时进行车的制造,最终进行组装,大大提高了效率。但是,具体到轮胎厂的一个流水线操作,仍然是有步骤的,不是离不开面向过程思想。
      因此,面向对象可以帮助我们从宏观上把握/从整体上分析整个系统。但是,具体到实现部分的微观操作(就是一个个方法),仍然需要面向过程的思路去处理
      面向对象和面向过程是相辅相成的,面向对象离不开面向过程。
  • 面向对象思考方式
    遇到复杂问题,先从问题中找名词(面向过程更多的是找动词),然后确立这些名词哪些可以作为类,再根据问题需求确定类的属性和方法,确定类之间的关系。

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

面向对象与面向过程

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 特殊方法和特性属性

特殊属性

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)