zoukankan      html  css  js  c++  java
  • Python Day 24 类属性与对象属性、初始化方法init、绑定方法与非绑定方法、OOP三大特性之继承、抽象与继承、存在继承关系后的属性查找、派生与覆盖、子类中重用父类的方法

      阅读目录

        内容回顾

        类属性与对象属性

        初始化方法init

        绑定方法

        对象之间交互练习

        OOP三大特性之继承

        抽象与继承

        一切皆为对象

        存在继承关系后的属性查找

        派生与覆盖

        子类中重用父类的方法

      ##内容回顾

    #1. xml模块
        解析xml格式的文档
        可扩展的标记语言
        <tag name="123" >text</tag>
    
        <tag1>
            <tag2>
            </tag2>
        </tag1>
        <tag/>
    
        1. tree =  ElementTree.parser("文件路径")
        root = tree.getroot()
        root.iter/find/findall
        直接遍历某标签 取出所有子级
    
        element.text = ""
        element.tag = "xxx"
        element.set("k","v")
    
        要删除需要先找父级
        父级.remove(子标签对象)
    
        父级.append(子标签)
    
        tree.write("路径")
    
    #2.面向对象的基本概念
        是一种编程思想,
        优点:扩展性高,复用性强,可维护性好
        缺点:编程复杂度提高了,需要先设计,结果无法准确预知
        使用场景:对扩展性要求高的程序
    
    
        面向过程:
        优点:复杂问题简单化,流程化
        缺点:扩展性极低,牵一发动全身,维护性相比OOP低
       使用场景:对扩展性要求低的程序
        将程序员从一个具体的操作者转变为指挥者
    
    #3.类与对象的关系
        对象是  具备某些特征和行为的结合体,是具体存在的某个事物
        类 某些具备相同特征和相同行为的集合体,是一种抽象概念
    
        创建类的语法
        class  大驼峰命名:
            属性用变量标识 (数据属性)
            名称=值
    
            行为用函数表示 (函数属性/方法)
            def 函数名称
    
         创建对象
            类名加括号
            Person()
    
    #4.属性的访问顺序
       优先访问对象自己的 如果没有 再访问类中的

      ##类属性与对象属性

    #1类中应该仅存储所有对象共有的内容
    
    ​    如所有人的国籍相同那就放到类中
    
    #2、对象中存储每个对象独有的内容
    
    ​    如每个人的名字都不同
    
    
    #示例
    # class Person:
    #     color = "black"
    #
    #     def eat(self):
    #         print("黑人哥们正在吃饭....")
    
    # 创建对象 也称之为实例化   产生的对象也称之为实例
    # p1 = Person()
    # print(p1)
    # print(p1.color)
    # print(p1.__dict__)
    #
    # p2 = Person()
    # print(p2)
    # print(p2.color)
    # print(p2.__dict__)
    #
    #
    # print(id(p1.color))
    # print(id(p2.color))
    # print(id(Person.color))
    
    
    # 当修改某一个对象的属性时 不会影响其他对象和类
    
    # p2.color = "yellow"
    # print(p2.color)
    # print(p1.color)
    # print(Person.color)
    #
    # Person.color = "red"
    # print(p1.color)
    # print(p2.color)
    
    # 类中应该仅存储 所有对象都相同的内容
    # 如果每个对象的属性都不同则应该放到对象自己的名称空间中
    #   如一个班级中所有学员的老师都相同
    
    
    class Student:
        school = "oldboy"
    
        def study(self):
            print("正在学习......")
    
    
    
    
    # stu1 = Student()
    # stu1.name = "张无忌"
    # stu1.age = 17
    
    
    # stu2 = Student()
    # stu2.name = "张翠山"
    # stu2.age = 37
    
    # 封装一个方法来初始化对象的属性
    # def my_init(stu,name,age):
    #     stu.name = name
    #     stu.age = age
    #
    # stu1 = Student()
    # stu2 = Student()
    #
    # my_init(stu1,"张无忌",17)
    # my_init(stu2,"张翠山",37)
    #
    # print(stu1.name)
    # print(stu2.name)

      ##初始化方法init

    # 什么是初始化方法
    
    -----用于为对象的属性设置初始值的函数
    
    # 为什么需要初始化方法
    
    -----在类的实例(对象)中,一些属性是必须存在的,就可以使用初始化函数来完成,比如`Student`对象中的`name`属性,它是必须的,用于唯一标识一个学生
    
    #执行过程:
    
    -----在创建对象时`Student("jack")`会申请新的内存空间用于保存对象数据,接着**自动调init函数
    
    #注意:
    
    -----`__init__`函数要求第一个参数必须是self,该参数表示需要被初始化的对象本身,这样就可以将name属性绑定到对象上
    
    -----可以将self改为其他任意的名称,但为了保证易读性通常是self,额外的参数须位于self之后
    
    -----有了`__init__`方法,在创建实例的时候,就不能传入空的参数了,必须传入与`__init__`方法匹配的参数,但`self`不需要传,Python解释器自己会把实例变量传进去:
    
    #示例一:
    class Student:
        def __init__ (self,name):
            print("init run")
            self.name = name
    # stu1 = Student()
    # 以上代码将抛出异常:TypeError: __init__() missing 1 required positional argument: 'name'
    stu1 = Student("jack")
    # 输出 init run
    print(stu1.name)
    # 输出 jack
    
    #示例二
    class Dog:
        def __init__(self,age,name,**kwargs):
            # print("init run")
            # print(self)
            self.age = age
            self.name = name
    
    # d = Dog() # === Dog.__init__(d)
    # # print(Dog.__init__)
    # # print(d)
    # # print(d.age)
    #
    #
    # Dog()
    
    d1 = Dog(1,"大花")
    d2 = Dog(2,"校花")
    
    print(d1.name)
    print(d2.name)
    
    #总结
    # 执行时机:当实例化产生对象时会自动执行该函数
    # 会自动传入需要初始化的对象
    # 初始化必须包含至少一个参数 用于表示对象本身
    # 该函数不允许有返回值 必须为None

       ##绑定方法

    # 什么是方法?
    
    先理清方法,函数,技能的关系:
    
    生活中对象的技能在程序中用函数表示
    
    函数在面向对象中称之为方法,换种称呼而已!
    
    如此说来,绑定方法也就是绑定函数
    
    # 为什么要绑定?
    
    在使用面向对象之前,数据与处理数据的函数是独立的没有任何联系,在调用函数时需要手动传入参数,如果要处理的数据有很多,参数的传递就是一个非常麻烦的事情,
    
    原始的处理方式:函数 传参
    
    ​    问题1 调用函数时传入参数,如果要处理的数据有很多,编写了很多重复的代码,代码的阅读性很差
    
    ​    问题2 后期如果每次处理的数据个数变多了,函数需要修改参数列表,导致以前写的所有代码都需要修改,扩展性非常差
    
    ​    问题3 如果传入了错误的数据,比如需要整型却传入了字符串,造成函数无法正常工作
    
    绑定方法的处理方式:
    
    ​    1.调用方法时传入对象,对象中包含了需要的所有数据,减少重复代码
    
    ​    2.后期数据变化时,修改类对象中的属性,方法中增加相应的处理代码,而方法参数不会发生变化,提高了扩展性
    
    ​    3.方法与对象进行绑定,没有对象则无法使用方法,并且在创建对象的初始化方法中,已经确定了各个属性数据时正确的,如此一来避免了传入使用错误数据执行函数造成的问题
    
    简单的说,就是将数据与处理数据的函数绑定在一起,没有数据则根本不需要处理数据的函数,反过来要执行处理数据的函数则必须提供要被处理的数据
    
    # 绑定方法与普通函数的区别
    
        1、当使用类调用时,就是一个普通函数 有几个参数就得传几个参数
    
        2、当用对象来调用时,是一个绑定方法了,会自动将对象作为第一个参数传入
    
    # 类中定义的函数分成两大类
    
    #1:绑定方法
    1.绑定到对象的方法:没有被任何装饰器装饰的方法。
    
             在类中定义的函数默认都是绑定到对象的方法
    
    ​       特点:参数的第一个必须是self 表示当前对象本身,使用对象来调用,调用时会自动传入对象
    
    ​    2.绑定到类的方法:用classmethod装饰器装饰的方法。
    
    ​       特点:参数的第一个必须是cls表示当前类本身,使用类名来调用,调用时会自动传入类
    
    #2:非绑定方法:用staticmethod装饰器装饰的方法
    
    ​    特点:不与类或对象绑定,类和对象都可以调用,但是没有自动传值那么一说。就是一个普通函数
    
    ​        不过由于作用域在类中所以需要使用类或对象类调用
    
    
    #绑定到类的方法与绑定到对象的方法总结
    
    #异同点:
    
    ​    相同
    
    ​        绑定对象调用时都有自动传参的效果
    
    ​        绑定到谁给谁就由谁来调用
    
    ​    不同
    
    ​        绑定到类的方法自动传入当前类
    
    ​        绑定到对象的方法自动传入当前对象
    
    #另外
    
    ​    绑定方法中的self 和 cls参数名 是可以随意修改的,但是self和cls是约定俗成的写法,为了提高可读性不建议修改
    #示例一
    class Person:
    
        def __init__(self,name,age):
            self.name = name
            self.age = age
    
        # 默认情况下 在类中定义的方法 都是绑定方法
        def say_hi(self):
            print("hello i am %s" % self.name)
            
    # 当你创建对象时 发生什么
    # 1.产生一个空对象 (名称空间)
    # 2.自动调用__init__方法 并且把这个对象以及额外的参数传入
    # p = Person("赵敏",17) # Person.__init__(p)
    # p.say_hi()
    #
    # p2 = Person("谢逊",50)
    # p2.say_hi()
    
    # 经常使用的数据定义为变量
    username = "jack"
    pwd = "123"
    db_name = "qq"
    
    atm_usr = "jerry"
    atm_pwd = "123"
    #用于登录数据库
    def login_data_base(username,pwd,db_name):
        print("%s登录%s数据库 密码为%s" % (username,db_name,pwd))
    
    def login_atm(usr,pwd):
        print("%s 登录了ATM 密码为%s" % (usr,pwd))
    
    login_data_base(username,pwd,db_name)
    login_data_base(username,pwd,db_name)
    
    login_atm(atm_usr,atm_pwd)
    login_atm(atm_usr,atm_pwd)
    
    #示例二
    class Student:
    
        school = "oldboy"
        def __init__(self,name):
            self.name = name
    
        # 默认情况下是绑定方法
        def study(self,a):
            print(self)
            print(a)
            # print("%s正在学习" % self.name)
    
    
    
    # print(Student.study)
    
    s = Student("jack")
    # print(s.study)
    s.study(100)
    
    # 对于类而言study就是普通函数
    # Student.study(1)
    
    # 而对于对象而言 他是一个绑定方法 当使用对象来调用时 会自动将对象作为第一个参数传入
    s1 = Student("rose")
    print(id(s1.study))
    print(id(s.study))
    
    print(s1.study)
    print(s.study)
    
    
    # print(s.school)
    # print(s1.school)
    
    #示例三
    import time
    class Person:
    
        country = "china"
        # init 也是绑定方法
        def __init__(self,name,age):
            self.name = name
            self.age = age
    
        @classmethod
        def info(cls):
            print(cls)
            print("info run")
    
        def sey_hi(self):
            print("hello i am %s" % self.name)
    
        # 是要输出这个类的一些信息
        @classmethod
        def class_info(cls):
            print("this class  %s  is module:xxx.py" % cls.__name__)
    
        # 输出当前时间
        # 不需要访问类也不需要访问对象 所以直接做成非绑定方法
        @staticmethod
        def show_time(self):
            print(time.localtime())
    
    # p = Person("rose",20)
    # p.info()
    #
    # print(Person)
    # print(Person.info)
    # Person.info()
    
    # p = Person("rose",10)
    # p.sey_hi()
    
    # p = Person("周芷若",20)
    #
    # Person.class_info(p)
    
    
    # Person.class_info()
    
    p = Person("rose",1)
    p.show_time(1)
    
    Person.show_time(2)
    #
    # print(Person.show_time)
    # print(p.show_time)
    
    
    #示例三
    '''
    ### 练习
    
    1.创建Student类
    
    2.拥有以下属性:
    姓名    性别    年龄    学校    班级
    
    3.拥有以下方法
    
    ​    save(name)         其作用是将这个对象序列化到文件中 
    
    ​    get_obj(name)    其作用是根据name从文件中反序列化为得到一个对象
    
    ​    分析save方法和get_obj 应该作为绑定给对象还是绑定给类
    '''
    import json,os
    class Student:
    
        school = "oldboy"
    
        def __init__(self,name,gender,age,cls_name):
            self.name = name
            self.gender = gender
            self.age = age
            self.cls_name = cls_name
    
        # 将对象的数据序列化到文件中
        def save(self):
            with open(self.name,"wt",encoding="utf-8") as f:
                json.dump(self.__dict__,f)
    
        @staticmethod
        def get_obj(name):
            if not os.path.exists(name):
                print("不存在该对象!")
                return None
            with open(name,"rt",encoding="utf-8") as f:
                dic = json.load(f)
                stu = Student(**dic)
                return stu
    
    # stu = Student("jack","woman",20,"py8")
    # stu.save()
    # stu = Student("lucy","woman",20,"py8")
    # stu.save()
    
    stu = Student.get_obj("jason")
    # print(stu.cls_name)
    
    

      #对象之间交互练习

    """
    需求设计王者荣耀中的英雄类,每个英雄对象可以对其他英雄对象使用技能
    具备以下属性
    英雄名称,等级,血量
    和Q_hurt,W_hurt,E_hurt 三个属性,表示各技能的伤害量
    具备以下技能
    Q W E
    三个技能都需要一个敌方英雄作为参数,当敌方血量小于等于0时角色死亡
    
    
    涉及到英雄对象
        属性:
        名字 等级 血量
        行为:
        Q  W  E
    需要一个英雄类
    
    """
    class Hero:
        def __init__(self,name,level,HP,Q_hurt,W_hurt,E_hurt):
            self.name = name
            self.level = level
            self.HP = HP
            self.Q_hurt = Q_hurt
            self.W_hurt = W_hurt
            self.E_hurt = E_hurt
    
        def Q(self,enemy):
            print("%s 对 %s 释放了 Q技能 造成了%s伤害" % (self.name,enemy.name,self.Q_hurt))
            self.attack(enemy, self.Q_hurt)
    
        def W(self,enemy):
            print("%s 对 %s 释放了 W技能 造成了%s伤害" % (self.name, enemy.name, self.W_hurt))
            self.attack(enemy, self.W_hurt)
    
        def E(self,enemy):
            print("%s 对 %s 释放了 E技能 造成了%s伤害" % (self.name, enemy.name, self.E_hurt))
            self.attack(enemy,self.E_hurt)
    
        def attack(self,enemy,hurt):
            enemy.HP -= hurt
            if enemy.HP <= 0:
                print("%s 已被%s击杀" % (enemy.name, self.name))
    
    
     # 创建两个英雄对象
    arso = Hero("亚瑟","15",1000,30,500,200)
    
    dj = Hero("妲己","15",800,300,10,800)
    
    #
    # dj.W(arso)
    # dj.Q(arso)
    # dj.E(arso)
    
    arso.Q(dj)
    arso.W(dj)
    arso.E(dj)
    
    dj.W(arso)
    dj.Q(arso)
    dj.E(arso)

      ##OOP三大特性之继承

    # 1.什么是继承
    
    继承是一种关系,通过继承关系,一个对象可以直接使用另一个对象拥有的内容,例如王思聪继承王建林,王思聪就可以使用王健林拥有的财产!
    
    被继承的一方称之为父,即王健林; 继承的一方称之为子,即王思聪
    
    OOP继承描述的是两个类之间的关系,通过继承,一个类可以直接使用另一个类中已定义的方法和属性;
    
    被继承的称之为父类或基类,继承父类的类称之为子类;
    
    #2.为什么需要继承
    极大的提高了代码的复用性
    #3.使用继承:
    至少需要两个类
    
    语法:
    
    class 子类名称(父类名称):
    
    ​    pass
    
    
    #示例1
    
    # class Teacher:
    #     school = "oldboy"
    #     def __init__(self,name,age):
    #         self.name = name
    #         self.age = age
    #
    #     def say_hi(self):
    #         print("hello i am %s" % self.name)
    #     def teach(self):
    #         print("正在教书......")
    #
    # class Student(Teacher):
    #     pass
    #
    # print(Student.school)
    # print(Student.say_hi)
    # print(Teacher.say_hi)
    #
    # s = Student("rose","123")
    # s.say_hi()
    #
    #
    # 在上述案例中通过继承 学生就拥有了老师的所有内容  但是学生不应该教书这个技能
    # 意味着这个继承关系 有问题 不合理
    # 需要先抽象 在继承

      ##抽象与继承

    #抽象与继承
    继承之后可以直接使用父类的属性和方法
    
    使用继承时 应该先抽象 在继承
    
    抽象指的是 将一系列类中相同的特征和相同行为抽取 形成一个新的类
    
    会产生一些与原本业务不想关的类
    
    站在不同角度会得到不同的抽象结果
    应当将`Teacher`与`Student`中完全相同的部分抽取出来,放到另一个类中,

    并让`Teacher与Student`去继承它,这个类称之为`公共父类` ,但是这个类与

    实际的业务需求是无关的在现实中也不实际存在,它的作用仅仅是存储相同代

    码以减少重复;这一过程我们称之为抽象。

    #综上所述,正确思路是:先抽象在继承
    #示例1
    # 抽象得到的公共父类
    class OldBoyPerson:
        school = "oldboy"
        def __init__(self,name,age):
            self.name = name
            self.age = age
    
        def say_hi(self):
            print("hello i am %s" % self.name)
    
    class Teacher(OldBoyPerson):
        def teach(self):
            print("正在教书......")
    
    
    class Student(OldBoyPerson):
        pass
    
    
    # 测试
    t = Teacher("owen",38)
    t.say_hi()
    t.teach()
    
    s = Student("歌王",20)
    s.say_hi()

      ##一切皆为对象

    #在python3中任何类都直接或间接继承自Object
    class Test:
        pass
    a = 10
    print(type(a))
    li = []
    print(type(li))
    
    t = Test()
    print(type(t))
    
    # a = 10   # a = int(10)
    # # a = int
    
    li = []
    # print(li)
    # li.append(10)
    # print(li)
    
    list.append(li,10)
    print(li)
    
    t = (1,2,3)
    print(type(t))
    
    # 一切皆对象指的是  在python中您所使用到的任何数据都是对象  int  float list dict 模块 包
    # import test1
    # print(type(test1))
    # import package
    # print(type(package))
    
    def t():
        pass
    # class a:
    #     pass
    # print(type(t))
    # print(type(a))

      ##存在继承关系后的属性查找

    #一个类必然继承另一个类,被继承的类也有可能继承了其他类,相当于C继承B,B又继承A
    
    #此时查找属性的顺序是:
    
    ​    对象本身的名称空间 - > 类的名称空间 -> 父类的名称空间 -> 父类的父类名称空间 ->object类
    
    #会沿着继承关系一直往后查找,直到找到为止,由于object是所有类的根类,所以如果找不着最后都会查找object类!
    
    #示例
    
    class A:
        name = "scot"
        # def __str__(self):
        #     print("111111111")
        #     pass
        pass
    
    class B(A):
        name = "rose"
        pass
    
    b = B()
    # b.name = "jack"
    
    print(b.name)
    # print(b.__str__())
    
    
    # 查找顺序
    # 对象自己 - > 所在的类 -> 所在类的父类 -> 父类的父类  -> object

      ##派生与覆盖

    # 什么是派生
    
    当父类提供的属性无法完全满足子类的需求时,子类可以增加自己的属性或非法,或者覆盖父类已经存在的属性,此时子类称之为父类的派生类;
    
    # 什么是覆盖
    
    在子类中如果出现于父类相同的属性名称时,根据查找顺序,优先使用子类中的属性,这种行为也称为`覆盖`
    
    从`Person`类派生出来的`Teacher`类
    
    #示例
    """
    #派生
    当一个类继承自另一个类 并且这个子类拥有与父类不同的内容 就称之为派生
    """
    # class A:
    #     def show_info(self):
    #         print("hello world!")
    #
    # class B(A):
    #     pass
    
    """
    #覆盖  (重写)
    子类中出现了与父类名称相同的属性 或方法   就会覆盖掉父类的属性或方法
    """
    class A:
        text = "123"
        def show_info(self):
            print("hello world!")
    
    class B(A):
        text = "321"
        def show_info(self):
            print("hello 你是DSB!")
        pass
    
    b = B()
    b.show_info()
    print(b.text)

    #示例2
    # 抽取老师和学生的相同内容 形成一个新的类,作为它们的公共父类
    class Person:
        def __init__(self,name,gender,age):
            self.name = name
            self.gender = gender
            self.age = age
        def say_hi(self):
            print("my name is %s age is %s gender is %s" % (self.name,self.age,self.gender))
    class Teacher(Person): #指定Teacher类继承Person类
        # Teacher类从Person类中继承到了say_hi方法 但是,老师打招呼时应当说出自己的职业是老师,所以需要
        # 定义自己的不同的实现方式
        def say_hi(self):
            print("hi i am a Teacher")
            #print("my name is %s age is %s gender is %s" % (self.name,self.age,self.gender))
            #上一行代码与父类中完全相同,可以直接调用父类提供的方法
            Person.say_hi(self)
    # 创建Teacher对象
    t1 = Teacher("Jack","man",20)
    t1.say_hi()
    #输出 hi i am a Teacher
    #     my name is Jack age is 20 gender is man

    #注
    在子类中,新建的重名的函数属性,在编辑函数内功能的时候,有可能需要重用父类中重名的那个函数功能,应该使用调用普通函数的方式,即:类名.func(),此时就与调用普通函数无异了,因此即便是self参数也要为其传值

      ##子类中重用父类的方法

    很多情况下 子类中的代码与父类中仅有小部分不同,却不得不在子类定义新的方法,这时候可以在子类中调用父类已有的方法,来完成大部分工作,子类仅需编写一小部分与父类不同的代码即可
    
    在子类中有两种方式可以重用父类中的代码
    
    1.使用类名直接调用 ,该方式与继承没有关系,即时没有继承关系,也可以调用
    
    2.使用super()
    
    #示例一
    class Person:
        text = "321"
        def __init__(self,name,age,gender):
            self.name = name
            self.age = age
            self.gender = gender
    
        def sleep(self):
            print("人类 午睡 躺着睡!")
    
        def say_hi(self):
            print("my name :%s my age :%s my gender: %s " % (self.name,self.age,self.gender),end="")
    
        
    class Student(Person):
        text = "123"
        def __init__(self,name,age,gender,number):
            #======================================================================重点在这里
            # 由于父类已经存在一个方法可以完成这个三参数的初始化
            # 所以可以直接调用父类的初始化完成这部分的初始化工作
            # 方法1
            # Person.__init__(self,name,age,gender) # 指名道姓的调用
    
            # 方法2  在py2中不支持
            super().__init__(name,age,gender)
    
            # py2的写法
            # super(Student, self).__init__(name,age,gender)
            self.number = number
            #======================================================================
    
        # 访问父类的属性
        def show_text(self):
            print(self.text)
            print(super().text)
    
        def say_hi(self):
            super().say_hi()
            print("my number: %s" %  self.number)
            # print("my name :%s my age :%s my gender: %s my number: %s" % (self.name, self.age, self.gender,self.number))
    
    
    s = Student("jack",20,"man","007")
    s.say_hi()
    # s.show_text()
    
    #示例二
    class Vehicle: #定义交通工具类
         Country='China'
         def __init__(self,name,speed,load,power):
             self.name=name
             self.speed=speed
             self.load=load
             self.power=power
    
         def run(self):
             print('开动啦...')
    
    class Subway(Vehicle): #地铁
        def __init__(self,name,speed,load,power,line):
            #super(Subway,self) 就相当于实例本身 在python3中super()等同于super(Subway,self)
            super().__init__(name,speed,load,power)
            self.line=line
    
        def run(self):
            print('地铁%s号线欢迎您' %self.line)
            super(Subway,self).run()
    
    class Mobike(Vehicle):#摩拜单车
        pass
    
    line13=Subway('中国地铁','180m/s','1000人/箱','',13)
    line13.run()
  • 相关阅读:
    谈谈Ext JS的组件——组件基类:Ext.Component
    谈谈Ext JS组件之引子
    安装Compass时不能访问服务器的问题
    Android 官方命令深入分析之Android Debug Bridge(adb)
    Android 官方命令深入分析
    Libgdx1.6.2发布,跨平台游戏开发框架
    Libgdx 1.6.1发布,跨平台游戏开发框架
    【翻译】Ext JS 6 Beta发布
    Springboot配置时间格式
    Thymeleaf在前台下拉列表获取后台传的值
  • 原文地址:https://www.cnblogs.com/liangzhenghong/p/10877811.html
Copyright © 2011-2022 走看看