• 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] 解决复杂问题:宏观上使用面向对象把握,微观处理上仍然是面向过程。

    面向对象与面向过程

    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),'
    *******
    ',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)
    
  • 相关阅读:
    android:gravity="center_vertical" 小注意
    架构设计:系统间通信(2)——概述从“聊天”开始下篇
    吴裕雄--天生自然轻量级JAVA EE企业应用开发Struts2Sping4Hibernate整合开发学习笔记:Spring_autowire
    吴裕雄--天生自然轻量级JAVA EE企业应用开发Struts2Sping4Hibernate整合开发学习笔记:Spring_tx
    吴裕雄--天生自然轻量级JAVA EE企业应用开发Struts2Sping4Hibernate整合开发学习笔记:Spring_TransactionProxyFactoryBean
    吴裕雄--天生自然轻量级JAVA EE企业应用开发Struts2Sping4Hibernate整合开发学习笔记:Spring_Transactional
    吴裕雄--天生自然轻量级JAVA EE企业应用开发Struts2Sping4Hibernate整合开发学习笔记:Spring_SimpleCache
    吴裕雄--天生自然轻量级JAVA EE企业应用开发Struts2Sping4Hibernate整合开发学习笔记:Spring_MethodCache
    吴裕雄--天生自然轻量级JAVA EE企业应用开发Struts2Sping4Hibernate整合开发学习笔记:Spring_key
    吴裕雄--天生自然轻量级JAVA EE企业应用开发Struts2Sping4Hibernate整合开发学习笔记:Spring_EhCache
  • 原文地址:https://www.cnblogs.com/lanxing0422/p/pythonday10.html
Copyright © 2020-2023  润新知