Python开发7——面向对象

一、面向对象与面向过程的区别

1、面向过程:核心是过程

    优点:复杂问题简单化

    缺点:可扩展性差(牵一发而动全身)

    应用场景:linux内核、httpd、git

2、面向对象:核心是对象

    优点:可扩展性强

    缺点:无法像面向过程一样准确知道什么阶段会有什么结果

    应用场景:需求经常变化的软件,与用户层交互多的软件、公司内部的软件、游戏、互联网软件

二、类与对象

1、python中一切皆为对象,且python3统一了类与类型的概念,类型就是类。

  说明:

    id(对象指向的内存地址)、type(类型)、value(对象的内容:值)

    is:身份运算符(比较id)

    ==:判断值(比较value)

x = 1
y = int(1)

print(id(x))
print(type(x))
print(x)

 2、类相关

  1)概述:

      类(Class): 用来描述具有相同的数据属性和方法属性的对象的集合。它定义了该集合中每个对象所共有的数据和方法。对象是类的实例。

      类变量:类变量定义在类中且在函数体之外,在所有实例化的对象中是公用的。id全一样(即内存地址都一样)。

      实例变量:定义在方法中的变量,只作用于当前实例的类。

      绑定到对象的方法:在类中直接定义的、没有被任何装饰器装饰的方法

      绑定到类的方法:装饰器@classmethod修饰的方法

      非绑定方法:装饰器@staticmethod修饰的方法

      实例化:创建一个类的实例,类的具体对象。

  2)声明类

      说明:创建一个类就会创建一个类的名称空间,用来存储类中定义的所有名字,这些名字称为类的属性。
         类有两种属性:数据属性和函数属性。

  3)类的两种作用:属性引用和实例化

      ①类的属性引用(类名.属性)

          Class.__dict__:查看类的属性字典(数据属性 与 函数属性),或者说名称空间

          Class.属性名:等同于 Class.__dict__['属性名']

      ②类的实例化(__init__与self):

            类名加括号就是实例化,会自动触发__init__函数的运行,可以用它来为每个实例定制自己的特征;

            self的作用是在实例化时自动将对象/实例本身传给__init__的第一个参数。

  4)对象/实例只有一种作用:属性引用

      说明:创建一个对象/实例就会创建一个对象/实例的名称空间,存放对象/实例的名字,称为对象/实例的属性

      对象.__dict__:查看对象的属性字典(对象的数据属性),或者说名称空间

      对象.属性名:等同于 对象.__dict__['属性名']

      print(obj.数据属性):会先在obj.__dict__中找这个属性,找不到再去Class.__dict__中找

#示例:每次实例化对象,count增1

class Student:
    count = 0
    def __init__(self):
        Student.count += 1

s1 = Student()
s2 = Student()
print(Student.count)

示例:

# 声明类
class Chinese:
    '''中国人'''
    language = 'Chinese'
    def __init__(self,name):  #不能有返回值
        self.name = name
    def talk(self):
        print('%s speak in %s'%(self.name,self.language) )

# 类的两种作用:属性引用和实例化
# 1、类的属性引用(类名.属性)
print(Chinese.language)
print(Chinese.__dict__['language'])
''' 2、类的实例化(__init__与self): 类名加括号就是实例化,会自动触发__init__函数的运行,可以用它来为每个实例定制自己的特征 self的作用是在实例化时自动将对象/实例本身传给__init__的第一个参数 ''' p = Chinese('aaa') # 对象/实例只有一种作用:属性引用 print(p.language) p.talk()

  5)类属性的补充

一:我们定义的类的属性到底存到哪里了?有两种方式查看
dir(类名):查出的是一个名字列表
类名.__dict__:查出的是一个字典,key为属性名,value为属性值

二:特殊的类属性
类名.__name__# 类的名字(字符串)
类名.__doc__# 类的文档字符串
类名.__base__# 类的第一个父类
类名.__bases__# 类所有父类构成的元组
类名.__dict__# 类的字典属性
类名.__module__# 类定义所在的模块
类名.__class__# 实例对应的类(仅新式类中)

3、绑定方法与非绑定方法

类中定义的函数分成两大类:

  1):绑定方法(绑定给谁,谁来调用就自动将它本身当作第一个参数传入):

    ①绑定到类的方法:用classmethod装饰器装饰的方法。

        为类量身定制

        类.boud_method(),自动将类当作第一个参数传入

        (其实对象也可调用,但仍将类当作第一个参数传入)

    ②绑定到对象的方法:在类中直接定义的、没有被任何装饰器装饰的方法,都是绑定到对象的方法。

        为对象量身定制

        对象.boud_method(),自动将对象当作第一个参数传入

        (属于类的函数,类可以调用,但是必须按照函数的规则来,没有自动传值一说)

  2)非绑定方法:用staticmethod装饰器装饰的方法

        不与类或对象绑定,类和对象都可以调用,但是没有自动传值一说。就是一个普通工具而已。

  3)练习:定义MySQL类

    ①对象有id、host、port三个属性

    ②定义工具create_id,在实例化时为每个对象随机生成id,保证id唯一

    ③提供两种实例化方式,方式一:用户传入host和port 方式二:从配置文件中读取host和port进行实例化

    ④为对象定制方法,save和get,save能自动将对象序列化到文件中,文件名为id号,文件路径为配置文件中DB_PATH;get方法用来从文件中反序列化出对象

      settings.py内容:

HOST='127.0.0.1'
PORT=3306

      test.py内容

import hashlib
import os
import pickle
import time
import settings

class MySQL:
    def __init__(self,host,port):
        self.id = self.create_id()
        self.host=host
        self.port=port

    # 绑定到对象的方法
    def save(self):
        file_path = r'%s%s%s' % (settings.db_path, os.sep, self.id + '.pickle')
        pickle.dump(self, open(file_path, 'wb'))

    # 绑定到对象的方法
    def get(self):
        file_path = r'%s%s%s' % (settings.db_path, os.sep, self.id + '.pickle')
        return pickle.load(open(file_path, 'rb'))

    # 绑定到类的方法
    @classmethod
    def from_conf(cls):
        print(cls)
        return cls(settings.HOST, settings.PORT)

    # 非绑定方法
    @staticmethod
    def create_id():  # 就是一个普通工具
        # 查看clock源码注释,指的是cpu真实时间,不要用time.time(),否则会出现id重复
        m = hashlib.md5(str(time.clock()).encode('utf-8'))
        return m.hexdigest()

print(MySQL.from_conf) # <bound method MySQL.from_conf of <class '__main__.MySQL'>>
conn=MySQL.from_conf() # <class '__main__.MySQL'>
conn.from_conf() # 类方法:对象也可以调用,但是默认传的第一个参数仍然是类:<class '__main__.MySQL'>

print(conn.id)
print(conn.create_id())
print(MySQL.create_id())

conn.save()
obj=conn.get()
print(obj.id)

  4)其他练习:

class Date:
    def __init__(self,year,month,day):
        self.year=year
        self.month=month
        self.day=day
    @staticmethod
    def now(): #用Date.now()的形式去产生实例,该实例用的是当前时间
        t=time.localtime() #获取结构化的时间格式
        return Date(t.tm_year,t.tm_mon,t.tm_mday) #新建实例并且返回
    @staticmethod
    def tomorrow():#用Date.tomorrow()的形式去产生实例,该实例用的是明天的时间
        t=time.localtime(time.time()+86400)
        return Date(t.tm_year,t.tm_mon,t.tm_mday)

a=Date('1987',11,27) #自己定义时间
b=Date.now() #采用当前时间
c=Date.tomorrow() #采用明天的时间

print(a.year,a.month,a.day)
print(b.year,b.month,b.day)
print(c.year,c.month,c.day)


#分割线==============================
import time
class Date:
    def __init__(self,year,month,day):
        self.year=year
        self.month=month
        self.day=day
    @staticmethod
    def now():
        t=time.localtime()
        return Date(t.tm_year,t.tm_mon,t.tm_mday)

class EuroDate(Date):
    def __str__(self):
        return 'year:%s month:%s day:%s' %(self.year,self.month,self.day)

e=EuroDate.now()
print(e) #我们的意图是想触发EuroDate.__str__,但是结果为
'''
输出结果:
<__main__.Date object at 0x1013f9d68>
'''
# 因为e就是用Date类产生的,所以根本不会触发EuroDate.__str__,解决方法就是用classmethod

import time
class Date:
    def __init__(self,year,month,day):
        self.year=year
        self.month=month
        self.day=day
    # @staticmethod
    # def now():
    #     t=time.localtime()
    #     return Date(t.tm_year,t.tm_mon,t.tm_mday)

    @classmethod #改成类方法
    def now(cls):
        t=time.localtime()
        return cls(t.tm_year,t.tm_mon,t.tm_mday) #哪个类来调用,即用哪个类cls来实例化

class EuroDate(Date):
    def __str__(self):
        return 'year:%s month:%s day:%s' %(self.year,self.month,self.day)

e=EuroDate.now()
print(e) #我们的意图是想触发EuroDate.__str__,此时e就是由EuroDate产生的,所以会如我们所愿
'''
输出结果:
year:2017 month:3 day:3
'''

三、继承

1、说明:新建的类可以继承一个或多个父类,父类又称为基类或超类,新建的类称为派生类或子类

2、Class.__bases__:查看Class的父类

3、python2与python3中继承的区别

    py2:
      class A(object):    # 新式类

        pass

      class A:        # 经典类

        pass
           py3:

      class A:        #(默认继承了object类)即所有类都是新式类

        pass

4、继承顺序(方法调用查找顺序(Class.mro():查看查找顺序))

    新式类:广度优先

    经典类:深度优先

5、子类调用父类中的方法时,py2与py3中调用方式的区别

    py2:

      super(subClass,self).func()

    py3:

      super().func()

      super(subClass,self).func()

四、组合

  raise TypeError('年龄必须是字整数类型')

  property

五、封装

六、多态

七、反射

  sys.modules[__name__]

  importlib

posted @ 2017-06-15 21:37  下一步之外  阅读(134)  评论(0)    收藏  举报