第十二章 面向对象

一、面向对象
面向对象的三大特性:
封装、继承、多态

1. 函数式编程和面向对象的对比:

举例:开发一个消息提醒的功能(邮件/短信/微信)
  函数:
def email(em, text):
    """
    发送邮件
    :return:
    """
    print(em, text)
def msg(tel, text):
    """
    发送短信
    :return:
    """
    print(tel, text)
def wechat(num, text):
    """
    发送微信
    :return:
    """
    print(num, text)
# 编写功能:向所有的联系方式发送天气
if True:
    msg('188888888', '今天有小雨')
    email('hao123@163.com.com', '今天有小雨')
    wechat('xxxx', '今天有小雨')
面向对象:
class Message:
    def email(self, em, text):
        """
        发送邮件
        :return:
        """
        print(em,text)
    def msg(self, tel, text):
        """
        发送短信
        :return:
        """
        print(tel,text)
    def wechat(self, num, text):
        """
        发送微信
        :return:
        """
        print(num,text)
# 编写功能:向所有的联系方式发送天气
if True:
    obj = Message()
    obj.email('hao123@163.com', '今天有小雨')
    obj.msg('188888888', '今天有小雨')
    obj.wechat('xxxx', '今天有小雨')

对比:

函数:定义简单、调用简单
面向对象:定义复杂、调用复杂 好处:归类,将某些类似的函数功能写在一起

总结:

  1 函数式编程可能会比面向对象好
  2. Python中支持两种编程方式
  3. 面向对象方式格式:
定义:
  class 类名: # 定义了一个类
     def 函数名(self): # 在类中编写一个“方法”
      pass
调用:
  x1 = 类名() # 创建了一个对象(实例化一个对象)
  x1.函数名() # 通过对象调用其中一个方法
构造方法:
  class Foo:
     def __init__(self, name): # 构造方法,目的进行数据初始化
      self.name = name
      self.age = 18
  obj = Foo("久末") # 给类名加括号,默认调用构造方法
通过构造方法,可以将数据进行打包,以后使用时,去其中获取即可
应用:
  1、将数据封装到对象中,以供自己在方法中使用
class FileHandler:
    def __init__(self, file_path):
        self.file_path = file_path
        self.f = open(self.file_path, 'rb')

    def read_first(self):
        # self.f.read()
        # ...
        pass

    def read_last(self):
        # self.f.read()
        # ...
        pass

    def read_second(self):
        # self.f...
        # ...
        pass
    
obj = FileHandler('C:/xx/xx.log')
obj.read_first()
obj.read_last()
obj.read_second()
obj.f.close()
  2、将数据封装到对象中,供其他函数调用
class FileHandler:
    def __init__(self, file_path):
        self.file_path = file_path
        self.f = open(self.file_path, 'rb')
    def read_first(self):
        # self.f.read()
        # ...
        pass
    def read_last(self):
        # self.f.read()
        # ...
        pass
    def read_second(self):
        # self.f...
        # ...
        pass
obj = FileHandler('C:/xx/xx.log')
obj.read_first()
obj.read_last()
obj.read_second()
obj.f.close()

2. 面向对象如何编写:

方式一:先归类,然后提取公共值
方式二:先在指定类中编写和当前类相关的所有代码,再提取公共值
三大特性:
封装:
将相关功能封装到一个类中
将数据封装到一个对象中
继承:
继承是为了复用,提高代码得重用性
先找子类(派生类),后找父类(基类)—— 子类和父类是相对存在的
先从子类中找,没有就从父类找
 
多继承(只存在python中的功能):左边更亲
多态:
多种形态或者多种状态
鸭子模型:只要可以嘎嘎嘎叫的就是鸭子
二、类成员
注:所有成员中,只有普通字段的内容保存对象中,即:根据此类创建了多少对象,在内存中就有多少个普通字段。而其他的成员,则都是保存在类中,即:无论对象的多少,在内存中只创建一份
class Foo:
    # 方法
    def __init__(self, name):
        # 实例变量/字段
        self.name = name
    # 方法
    def func(self):
        pass
# obj,Foo类的对象
# obj,Foo类的实例
obj = Foo('jiumo')

1. 变量(字段):

字段包括实例变量和静态变量,他们在定义和使用中有所区别,而最本质的区别是内存中保存的位置不同
由上图可是:
  • 静态变量在内存中只保存一份
  • 实例变量在每个对象中都要保存一份
应用场景: 通过类创建对象时,如果每个对象都具有相同的变量,那么就使用静态变量
# 类变量/实例变量
class Foo:
    # 类变量/静态字段
    country = '中国'
    # 方法
    def __init__(self, name):
        # 实例变量/字段
        self.name = name
    def func(self):
        pass

obj1 = Foo('jiumo')
obj2 = Foo('王XX')
obj1.country = '美国'
print(obj1.country)
print(obj2.country)

obj1 = Foo('jiumo')
obj2 = Foo('王XX')
Foo.country = '美国'
print(obj1.country)
print(obj2.country)
==>美国
中国
美国
美国
  实例变量(字段)
    - 公有实例变量(字段)
# 公有实例变量(字段)
class Foo:
    def __init__(self, name):
        self.name = name
        self.age = 123
     # 内部调用
    def func(self):
        print(self.name)
obj = Foo("jiumo")
# 外部调用
print(obj.name) # jiumo
print(obj.age)  # 123
obj.func()  # jiumo
    - 私有实例变量(字段)
# 私有实例变量(字段)
class Foo:
    def __init__(self, name):
        # 定义为私有
        self.__name = name
        self.age = 123
     # 内部调用
    def func(self):
        print(self.__name)
obj = Foo("jiumo")
# 外部不可直接调用
# print(obj.name)     # 报错AttributeError: 'Foo' object has no attribute 'name'
print(obj.age)
obj.func()  # 间接访问:让func帮助执行内部私有的__name  就相当在类的内部有认识的”关系” 间接的得到想要的内容
  类变量(静态字段)
    - 公有类变量(静态字段)
# 默认公有类变量(字段)
class Foo:
    country = "中国"
    def __init__(self):
        pass
    def func(self):
        # 内部调用
        print(self.country)
        print(Foo.country)  # 推荐
# 外部调用
print(Foo.country)    # 中国
    - 私有类变量(静态字段)
# 私有
class Foo:
    __country = "中国"
    def __init__(self):
        pass
    def func(self):
        # 内部调用
        print(self.__country)
        print(Foo.__country)  # 推荐
# 外部无法直接调用
# print(Foo.__country)
obj = Foo()
obj.func(
准则:
实例变量(字段)访问时,使用对象访问,即:obj1.name
类变量(静态字段)访问时,使用类访问,即:Foo.country(实在不方便时才使用对象)
易错点:
obj1 = Foo('jiumo')
obj2 = Foo('王XX')

obj1.name = '老王'
print(obj1.name)
print(obj2.name)
==>老王
王XX
什么时候用类变量:
当所有对象中有共同的字段,并且值永远同步,那么可以使用类变量
父类中的私有字段子辈无法知道,但是可以通过调用父类得到
# 子类也不能直接调用父类的私有字段
class Bsae(object):
    __secret = "RMB"
class Foo(Bsae):
    def func(self):
        # print(self.__secret)  # AttributeError: 'Foo' object has no attribute '_Foo__secret'
        print(Foo.__secret)     # AttributeError: type object 'Foo' has no attribute '_Foo__secret'
obj = Foo()
obj.func()
# 同样可以利用间接调用的方法得到私有字段
class Bsae(object):
    __secret = "RMB"
    def money(self):
        print(Bsae.__secret)
class Foo(Bsae):
    def func(self):
        pass
obj = Foo()
obj.money()

2. 方法

实例方法
静态方法
1. 定义时:
- 方法上方写 @staticmethod
- 方法参数可有可无
2. 调用时:
- 类.方法名() # 推荐使用
- 对象.方法名()
3. 什么时候使用静态方法:
- 无需调用对象中封装的值
- 类方法
1. 定义时:
- 方法上方写:@classmethod
- 方法的参数:至少有一个cls参数
2. 执行时:
- 类名.方法名 # 默认会将当前类传到参数中
3. 什么时使用类方法:
- 如果在方法中会使用到当前类,那么就可以使用类方法
# 没必要写实例方法
class Foo(object):
    def __init__(self, name):
        self.name = name
    def func(self):
        print('实例方法')   # 没有用到构建方法中的值
obj = Foo('jiumo')
obj.func()
# 有意义的实例方法
class Foo(object):
    def __init__(self, name):
        self.name = name
    # 实例方法,最少有一个参数
    def func(self):
        print(self.name)    # wb
    # 静态方法,可以没有参数.  如果方法中无需使用对象中封装的值,那么就可以使用静态方法
    # 可以通过类直接调用,不需要实例化类这部操作
    @staticmethod
    def display():
        print('静态方法')    # 静态方法
    # 类方法
    @classmethod
    def train(cls, name):
print(cls)        # <class '__main__.Foo'>
        print(name)        # 类方法
# 实例方法
obj = Foo('wb')
obj.func()
# 静态方法的调用
Foo.display()
# 类方法的调用
Foo.train('类方法')

==>实例方法
wb
静态方法
<class '__main__.Foo'>
类方法
  方法的成员修饰符:方法也有公有方法和私有方法之分 用法同变量的成员修饰符
# 私有的实例方法
class Foo(object):
    def __init__(self):
        pass
    def __display(self,arg):
        print('私有方法',arg)
    def func(self):
        self.__display(123)
obj = Foo()
# obj.__display(123) # 无法访问
obj.func()
# 私有的静态方法
class Foo(object):
    def __init__(self):
        pass
    @staticmethod
    def __display(arg):
        print('私有静态 方法',arg)    # 私有静态 方法 123
    def func(self):
        Foo.__display(123)
    @staticmethod
    def get_display():
        Foo.__display(888)  #私有静态 方法 888
# Foo.__display(123) 报错
obj = Foo()
obj.func()

Foo.get_display()

3. 属性

class Foo(object):
    def __init__(self):
        pass
    @property
    def start(self):
        return 1
    @property
    def end(self):
        return 10
obj = Foo()
print(obj.start)
print(obj.end)
1 编写时:
- 方法上方写@property
- 方法参数:只有一个self参数
2 调用时:
- 无需加括号 对象.方法
3 应运场景:
- 对于简单的方法,当无需传参且有返回值时

4. 特殊方法

class Foo():
    # 1.
    def __init__(self,a1, a2):
        self.a1 = a1
        self.a2 = a2
    # 2.
    def __call__(self, *args, **kwargs):
        print('wb', args, kwargs)
        return 123
    # 3.
    def __getitem__(self, item):
        print(item)
        return 123
    # 4.
    def __setitem__(self, key, value):  # 无返回值
        print(key, value, 123)
    # 5.
    def __delitem__(self, key): # 无返回值
        print(key)
    # 6.
    def __add__(self, other):
        return self.a1 + other.a1
    # 7.
    def __enter__(self):
        print('开始代码')
        return 999
    # 8.
    def __exit__(self, exc_type, exc_val, exc_tb):
        print("结束代码")

    1.类名() 自动执行 __init__
    obj = Foo(1, 2)
    2.对象() 自动执行 __call__
    ret = obj(2018, 9, time = 2)
    print(ret)
    3.对象[] 自动执行 __getitem__
    ret = obj['wang']
    print(ret)
    4.对象['xx'] = 11 自动执行 __setitem__
    obj['k1'] = 123
    5.del 对象['xx'] = 11 自动执行 __delitem__
    del obj['wang']
    6.对象+对象       自动执行 __add__
    9. with 对象    自动执行__enter__ 和 __exit__
    obj = Foo(1, 2)
    with obj as f:
        print(f)
        print('内部代码')

    10.真正的构造方法 __new__
    class Foo(object):
        def __init__(self,a1, a2):
            print(1)
            self.a1 = a1
            self.a2 = a2
        def __new__(cls, *args, **kwargs):
            print(2)    # 执行到此处中断了
    Foo(1, 2)
三、反射
python中的反射功能是由以下四个内置函数提供的:
  - getattr(): 根据字符串为参数(第二个参数),去对象(第一个参数)中去寻找与之同名的成员
  - hasattr():根据字符串的形式,去判断对象中是否有成员
  - setattr():根据字符串的形式,动态的设置一个成员(内存)
  - delattr():根据字符串的形式,动态的删除一个成员(内存)
class Foo(object):
    def __init__(self):
        self.name = 'jiumo'
    def func(self):
        return 'func'
obj = Foo()
# 检查是否含成员变量
print(hasattr(obj, 'name'))    # True
print(hasattr(obj, 'func'))    # True
# 获取成员
print(getattr(obj, 'name')) # jiumo
print(getattr(obj, 'func')) # <bound method Foo.func of <__main__.Foo object at 0x0000016810C9C908>>
# 设置成员
setattr(obj, 'age', 18)
print(getattr(obj, 'age'))  # 18
setattr(obj, 'show', lambda num: num + 1)
# print(getattr(obj, 'show')) # <function <lambda> at 0x0000016811EA47B8>
# 删除成员
delattr(obj, 'name')
print(hasattr(obj, 'name')) # Fals
delattr(obj, 'show')
print(hasattr(obj, 'show')) # False

# 反射实例说明:
class Foo(object):
    func_lst = ['f1', 'f2', 'f3']
    def f1(self):
        print('注册成功')
    def f2(self):
        print('登陆成功')
    def f3(self):
        print('注销成功')
obj = Foo()
while True:
    print("""
    选择需要的功能:
        1. 注册
        2. 登陆
        3. 注销
    """)
    val = int(input("请输入要选择的功能:"))
    try:
        func_name = obj.func_lst[val-1]

        if hasattr(obj, func_name):
            func = getattr(obj, func_name)
            func()
        break
    except Exception:
        print("请输入正确的序号!")
原文地址:https://www.cnblogs.com/jiumo/p/9545056.html