"""面向对象"""
# -----------------------------------------------------------
'''
--几种开发过程对比
# 面向过程：就是我们一开始学习的，按照解决问题的步骤去写代码 【根据业务逻辑从上到下去写代码】
# 函数式：将某功能代码封装到函数中，日后便无需重复编写，仅调用函数即可。对面向过程进行优化
# 面向对象编程：oop [object oriented programming] 是一种python的编程思路。
             将数据与函数绑定到一起，进行封装，这样能够更快速的开发程序，减少了重复代码的重写过程
'''
# -----------------------------------------------------------
'''
--面向过程 & 面向对象 的区别：

  A.eg：洗车这件事，面向过程关注的是‘洗’这件事，怎么洗用什么洗分几个步骤洗；而面型对象关注的是将‘车’直接扔给谁洗，car.wash（），像黑匣子，用就完事，不关心里面怎么实现怎么洗车，只关心谁来洗
    面向过程：在思考问题的时候, 首先分析'怎么按照步骤去实现' 然后将问题解决拆解成若干个步骤，并将这些步骤对应成方法一步一步的最终完成功能
    面向对象：关注的是设计思维，我们不关心怎么洗（eg:找洗车店，给钱洗车）
    洗车第一种方式:强调的是步骤、过程、每一步都是自己亲自去实现的,这种解决问题的思路我们就叫做面向过程
    洗车第二种的方式：强调的是洗车店，由洗车店去帮我们洗车，过程不用我们自己去操心，对我们而言,我们并不必亲自实现整个步骤只需要调用洗车店就可以解决问题，这种解决问题的思路就是面向对象。

    面向过程的关注点是: 怎么做
    面向对象的关注点是: 谁来做（这个谁就是对象）

  B.eg：出差这件事。
    小明-什么时间-买什么票（高铁 汽车 飞机）-北京-酒店-回程时间(亲力亲为的去做) 【面向过程】不足：小明没有更多的精力去考虑别的事情
    boss-秘书（小明需要考虑的事情）-财务-目的：北京 【面向对象】优点：boss有更多的精力去处理其他的时候，创造更多的价值
    
  C.从计算机的角度来看，向过程不适合做大项目，而面向对象合适做更大项目的设计
  
  D.eg：有一天你想吃鱼香肉丝了，怎么办呢？你有两个选择
    1.自己买材料，肉，鱼香肉丝调料，蒜苔，胡萝卜等等然后切菜切肉，开炒，盛到盘子里。
    2.去饭店，张开嘴：老板！来一份鱼香肉丝！
    看出来区别了吗？这就是  1是面向过程，2是面向对象。
    面向对象有什么优势呢？首先你不需要知道鱼香肉丝是怎么做的，降低了耦合性。如果你突然不想吃鱼香肉丝了，想吃洛阳白菜，对于1你可能不太容易了，还需要重新买菜，买调料什么的。
    对于2，太容易了，大喊：老板！那个鱼香肉丝换成洛阳白菜吧，提高了可维护性。总的来说就是降低耦合，提高维护性！
    
    {面向过程是具体化的，流程化的，解决一个问题，你需要一步一步的分析，一步一步的实现。
    {面向对象是模型化的，你只需抽象出一个类，这是一个封闭的盒子，在这里你拥有数据也拥有解决问题的方法。需要什么功能直接使用就可以了，不必去一步一步的实现，至于这个功能是如何实现的，管我们什么事？我们会用就可以了。
    {面向对象的底层其实还是面向过程，把面向过程抽象成类，然后封装，方便我们我们使用的就是面向对象了。

# 优缺点对比
  -面向过程：
  优点：性能比面向对象好，因为类调用时需要实例化，开销比较大，比较消耗资源。
  缺点：不易维护、不易复用、不易扩展.
  -面向对象：
  优点：易维护、易复用、易扩展，由于面向对象有封装、继承、多态性的特性，可以设计出低耦合的系统，使系统 更加灵活、更加易于维护 .
  缺点：性能比面向过程差
'''
# -----------------------------------------------------------
'''
--面向对象与面向对象编程
# 面向对象：
  按人们认识客观世界的系统思维方式,采用基于对象(实体) 的概念建立模型,模拟客观世界分析、设 计、实现软件的办法。
# 面向对象编程(Object Oriented Programming-OOP):
  是一种解决软件复用的设计和编程方法。这种方法把软件系统中相近相似的操作逻辑和操作 应用数据、状态,以类的形式描述出来,以对象实例的形式在软件系统中复用,以达到提高软件开发效率的作用。
'''
# -----------------------------------------------------------
'''
--类 & 对象
# 引入
  类：就是一个模板，模板里可以包含多个函数，函数里实现一些功能
  对象：则是根据模板创建的实例，通过实例对象可以执行类中的函数
# 比较
  类相当于制造汽车的图纸，用这个图纸制造的汽车相当于对象
  
# 类 的组成：
  类(Class)由3个部分构成
  1.类的名称: 类名 
  2.类的属性: 一组数据 (相当于类的特征)
  3.类的方法(行为): 允许对进行操作的方法 
  eg：创建一个'人'类
      事物名称(类名)： 人(Person) 
      属性： 身高(height)、年龄(age)、体重、发型...
      方法： 吃(eat)、跑(run)、睡、跳、说话...
      
# 类的定义：
  具有相同(或者类似)属性和行为的一系列对象的集合都可以抽像出一个类。
  /类是具有一组 相同或者相似特征【属性】和行为【方法】的一系列[多个]对象组合。
  现实世界------->计算机世界
  行为---------->方法
  特征---------->属性

# 对象的定义: 
  对象是实实在在的一个东西，类的实例化，具象化
  
# 类和对象的关系：
  类是对象的抽象化；而对象是类的一个实例。
  
# eg:抽象坦克大战中的类
     坦克 - 类名：Tank   属性：血量 类型   方法：发射子弹
     墙 - 类名: Wall   属性：血量 类型   方法：阻挡
     子弹  - 类名：Bullet   属性：杀伤力
'''
# -----------------------------------------------------------
'''
--定义类和对象
# 类结构  类名  属性  方法
# 定义方法
  class 类名:
       属性
       方法

# 当我们创建完一个类时，这个类式没有意义的，类式不在内存中创造任何空间的，不开辟空间。只有这个类创建了对象的时候，才会在内存中给对象分配一个内存空间。对象就是类的实例化/具象化。

# 创建对象【类的实例化】
# 规则格式  对象名 = 类名()

'''
# -----------------------------------------------------------
'''
--实例方法与属性

# 实例方法
# 定义：在类的内部，使用def关键字来定义；第一个参数默认是self【self名字标识可以是其他的名字，但是这个位置必须被占用】
# 特点：
  1.方法必须包含参数 self，且为第一个参数。
  2.实例方法，使用def关键字定义，第一个形参默认传实例对象本身，一般使用self作为第一个参数。
  3.一个类里面可以有多个实例方法；
  4.实例方法归于类的实例所有
# 实例方法 & 普通方法 的对比：
  实例方法定义在类的内部，实例方法归实例（对象）所有；普通方法不在类中定义，在类的外部定义

# 类属性
# 定义：在类的内部定义的变量。定义在类的里面，方法外面的属性称之为类属性

# 实例属性
# 定义：定义在实例方法里面使用self引用的属性称之为实例属性。在方法内部定义的【通过类似于self.变量名】 变量，是实例属性
'''
# -----------------------------------------------------------
'''
--__init__方法
# __init__(self) 方法，初始化方法，实例化对象的时候自动调用，完成一些初始化设置。
# 实用性：
  1.比如要给新创建的对象用input起名,如果类属性下直接name=input(),就会在完成类创建后直接让你输入名字，进而让这一类对象全叫这个名字Python_init;
    而在init下用name=input()就会在每次创建对象的时候询问你名字,每个对象就可以叫独有的名字。
  2.类属性相当于是公有的，你修改类属性的值，随后创建的对象属性都会改变；
    而通过__init__定义的属性的值，你对属性修改，只会对这一个对象生效。
# 总结：
  1. python自带的内置函数，具有特殊的函数，使用双下划线包起来的【魔术方法】
  2. 是一个初始化的方法，用来定义实例属性和初始化数据的，在创建对象的时候自动调用，不用手动去调用
  3. 利用传参的机制可以让我们定义功能更加强大并且方便的类
  
--__init__传参
# 如果init方法里面的属性固定了，每个类创建出来的对象属性都一样，这个时候我们是不是考虑将属性当参数在实例化对象的时候传进去，让类更通用？
# __init__(self)中，默认有1个参数名字为self，如果还需要传两个实参，那么应该写成__init__(self，x, y)。

'''
# -----------------------------------------------------------
'''
--理解self
# self和对象指向同一个内存地址，可以认为self就是对象的引用。
# self传参问题：
  所谓的self，可以理解为对象自己，某个对象调用其方法时，python解释器会把这个对象作为第一个参数传递给self，所以开发者只需要传递后面的参数即可。
# 实例化对象时，self不需要开发者传参，python自动将对象传递给self
# self特点总结：
  1. self只有在类中定义实例方法的时候才有意义,在调用时候不必传入相应的参数，而是由解释器自动去指向
  2. self的名字是可以更改的，可以定义成其他的名字，只是约定俗成的定义成了self
  3. self指的是类实例对象本身，相当于java中this
'''
# -----------------------------------------------------------
'''
--魔术方法
# 在python中，有一些内置好的特定的方法，方法名是“__xxx__”,在进行特定的操作时会自动被调用，这些方法称之为魔法方法。
# 常见的魔法方法：
  __init__方法：初始化一个类，在创建实例对象为其赋值时使用。
  __str__方法：在将对象转换成字符串str(对象)测试的时候，打印对象的信息。
  __new__方法：创建并返回一个实例对象，调用了一次，就会得到一个对象。【单例】
  __class__方法：获得已知对象的类 ( 对象.__class__)。
  __del__方法：对象在程序运行结束后进行对象销毁的时候调用这个方法，来释放资源。
  ……
  
# __new__和__init___函数的区别
  1.__new__类的实例化方法，必须要返回该实例reture，否则对象就创建不成功
  2.__init__用来做数据属性的初始化工作，也可以认为是实例的构造方法，接受类的实例 self 并对其进行构造
  3.__new__至少有一个参数是 cls 代表要实例化的类 ，此参数在实例化时由python解释器自动提供
  4.__new__函数执行要早于__init__函数。 要先创建对象，使对象存在了再去对象初始化。
'''







# -----------------------------------------------------------类的创建
# 定义类用大驼峰定义法，第一个字母大写
class Person:
    '''
    对应人的特征
    '''
    name = 'xiaoming'
    age = 20
    '''
    对应人的方法
    '''

    def eat(self):
        print('eat')
        pass

    def run(self):
        print('run')
        pass


# 创建一个对象/类的实例化
xm = Person()
xm.eat()  # 调用函数
xm.run()
print('name is {}'.format(xm.name))
xm.name = 'lalala'
print('name is {}'.format(xm.name))


# -----------------------------------------------------------实例方法
class Animal(object):
    def test(self):
        print("我是实例方法")
        pass
    def show(ssss):  # 用其他字符也可以
        print("我是实例方法show")
        pass
    pass

a = Animal()
a.show()
a.test()

class Person:
    '''
    对应人的特征
    '''
    age = 20
    '''
    对应人的方法
    '''
    def __init__(self):
        self.name='xioaming'  # 实例属性，这个属性归属于实例
        pass
    # 类属性和实例属性最好不要重名。在分配内存空间的时候两者有区别。实例属性的优先级比类属性高
    def eat(self):
        print('eat')
        pass

    def run(self):
        print('run')
        pass

# 创建一个对象/类的实例化
xm = Person()
xm.eat()  # 调用函数
xm.run()
print('name is {}'.format(xm.name))
xm.name = 'lalala'
print('name is {}'.format(xm.name))


# -----------------------------------------------------------
#1.
class People:
    def eat(self):
        '''
         # 实例方法.吃的行为
        :return:
        '''
        print('喜欢吃榴莲')
    pass

xq = People()
xq.name = 'xiaoqian'  # 实例属性
xq.age = 16  # 实例属性
xq.sex = 'girl'  # 实例属性
xq.eat()
print(xq.name, xq.age, xq.sex)

xl = People()
xl.name = 'xiaoli'  # 实例属性
xl.age = 18  # 实例属性
xl.sex = 'boy'  # 实例属性
xl.eat()
print(xl.name, xl.age, xl.sex)
# xl和xq两个实例没有任何关系，相互独立，xl要想打印mane,age,sex等基本信息需要向上面一样重新添加一次
# 他们只有相同的实例方法eat
# 如果有n个这个对象  被实例化  那么就需要添加很多次这样的属性了 显然是比较麻烦
# 所以下面用到__init()__方法

#2.
class People:
    def __init__(self): # 凡是__开头结尾的方法就是python中内置好的方法，叫魔术方法。
        '''
        统一声明实例属性
        '''
        self.name = 'xiaoqian'
        self.age = 20
        self.sex = 'girl'
        pass
    pass

xq = People()  # 在创建新对象的时候 是自动执行的__init__方法的
print(xq.name, xq.age, xq.sex) # 直接输出init方法中定义的默认值

xl = People()
xl.name = 'xiaoli'  # 修改默认的实例属性
xl.age = 18  # 修改默认的实例属性
xl.sex = 'boy'  # 修改默认的实例属性
print(xl.name, xl.age, xl.sex)
# 这时定义实例后就可以直接调用这几个实例属性name,age,sex赋值即可，而不用重新定义、声明属性再赋值

#3.
class People:
    def __init__(self, name, age, sex):
        '''
        实例属性的声明
        '''
        self.name = name
        self.age = age
        self.sex = sex
        pass
    def eat(self, food):
        '''
        吃的行为
        :return:
        '''
        print(self.name + '喜欢吃' + food)
    pass

wm = People('wangming', 'boy', 18)
wm.eat('橘子')
print(wm.name,wm.age,wm.sex)

xh = People('xiaohua', 'girl', 18)
xh.eat('草莓')
print(xh.name, xh.age, xh.sex)
# 这样每次生成实例时会给实例属性赋值，每个实例就是独一无二的了
# 而且看起来更加规整方便


# ----------------------------------------------------------- self理解
#
class Person:
    '''
    定义类
    '''
    def eat(self):  # 定义式有参数
        '''
        实例方法
        :return:
        '''
        print(id(self))
        pass
    pass

xw = Person()
xw.eat()    # 调用时并没有传递self对象，它直接就有
print(id(xw))

# 两个id是一个地址，所以self其实就是指这个对象本身；所以上面调用时不用传递参数self，因为它本身就是self所以不用再传这个参
# 类似于java中的this
# 实例化对象时，self不需要开发者传参，python自动将对象传递给self

class Person:
    '''
    定义类
    '''
    def eat(self, name, food):  # 定义式有参数
        '''
        实例方法
        :return:
        '''
        print('{}喜欢吃{}'.format(name,food))
        pass
    pass

xw = Person()
xw.eat('xiaoming','liulian')    # 不需要传递self，因为xw这个对象本身就是self

#
class Person:
    def __init__(self, pro):
        '''
        :param pro: 专业
        '''
        self.pro = pro  # 实例属性的定义
    def eat(a, name, food):
        print('{} like {}. pro is {}'.format(name, food, a.pro))
        # 这里pro为实例属性，a这里其实就指的是实例对象本身/self，所以只需a.就可以调出pro
        pass
    pass

xw = Person('computer')  # 这里其实就是init要传的参数
xw.eat('xaiowang', 'pingguo')


# -----------------------------------------------------------
# __str__
class Person:
    def __init__(self, pro, name, food):
        self.pro = pro  # 专业
        self.name = name
        self.food = food
        pass
    def eat(self):
        print('{} like {}. pro is {}'.format(self.name, self.food, self.pro))
        pass
    def __str__(self):
        '''
        打印对象 自定义对象是内容格式的
        :return:
        '''
        return '%s喜欢吃%s，修的专业是:%s'%(self.name, self.food, self.pro)
    pass


xw = Person('计算机','小明','榴莲')  # 这里其实就是init要传的参数
print(xw)
# 若没有__str__，print(xw)输出的是实例化的地址
# __str__很方便，查看对象信息的时候可以用它

#__new__
class Person:
    def __init__(self, pro, name, food):
        self.pro = pro  # 专业
        self.name = name
        self.food = food
        print('----init------')
        pass
    def eat(self):
        print('{} like {}. pro is {}'.format(self.name, self.food, self.pro))
        pass
    def __str__(self):
        '''
        打印对象 自定义对象是内容格式的
        :return:
        '''
        return '%s喜欢吃%s，修的专业是:%s'%(self.name, self.food, self.pro)
    def __new__(cls, *args, **kwargs):
        '''
        创建对象实例的方法 【其实在实例化对象的过程中，这个方法是必须要执行的；没有写会自动生成默认的】
        每调用一次就会生成一个新的对象，cls就是class的缩写

        new函数是创建对象时第一个执行的函数
        场景：可以控制创建对象的一些属性限定；经常用来做单例模式的时候来使用
        :param args:
        :param kwargs:
        '''
        print('----new------')
        # 创建对象
        return object.__new__(cls)   # 在这里是真正创建对象实例的 | 超对象.new(cls指当前类)
        # 如果没有这一句的话，打印时就只有----new------，没有----init------和小明喜欢吃榴莲，修的专业是:计算机
        # 因为new主要就是用来创建对象，创建对象的核心逻辑在new中体现，若没有上面这一句则只是打印了'----new------'这句话，并没有创建新对象，没有对象就当然没有init函数
        pass
    pass

xw = Person('计算机','小明','榴莲')
print(xw)