前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Python的面向对象

Python的面向对象

作者头像
ZONGLYN
发布2019-08-08 10:36:21
4100
发布2019-08-08 10:36:21
举报
文章被收录于专栏:程序萌部落程序萌部落
面向对象
目的:
    写出有意义的面向对象的代码,其作用就是封装代码
定义时注意:
    命名规范 Student、StudentPages
    类体不能什么都不写,要写pass

定义示例:
    class Student():
        # 开始类体的编写
        name = ''   
        age = 0

        def print_file():
            print('age = '+str(age))
    stu = Student() #不需要使用new 来实例化这个类
调用类的方法:
    stu.print_file()
    注意上述调用会报错  
        # TypeError: print_file() takes 0 positional arguments but 1 was given
    做如下修改:
        def print_file(self):
            print('age = '+str(age))
    仍然报错,报错age没有定义
    继续修改,改完正确运行
        def print_file(self):
            print('age = '+str(self.age))

    正确示例:
        class Student():
            name = ''   
            age = 0
            def print_file(self):
                print('age = '+str(self.age))
        stu = Student() 
        stu.print_file()
        # 或者直接用:Student().print_file()
    注意:
        上述类体中,对于print_file函数,不能在类体里调用
        写类的模块,最好是只写类,然后通过其他模块来实例化调用什么的
            from c1 import Student
            Student().print_file()
            Student().age
        注意:
            如果c1.py中同时包含对Student类的实例化和调用,那么上述import时也会执行c1.py中的示例化和调用
            所以,最好是模块和类分开,便于调用时的清晰
方法 和 函数:
区别:
    方法是语言设计层面的考量,应用起来没什么区别
    类中的函数应该叫‘方法’,模块中的函数就叫‘函数’
    类中的变量应该叫‘数据成员’,模块中变量叫‘变量’

类和对象 通过实例化联系在一起

什么是类:
    就是数据及一些操作的有意义的封装,可以体现出数据特征和行为特征
    行为要联系主体,体现在类的设计中要具有现实意义
什么是对象:
    表示具体的类对象,类本身可以实例化多种多样的对象

通过实例化来创造对象的多样性,依靠类的构造函数实现

    class Student():
        name = ''   
        age = 0
        def __init__(self): # 至少需要添加self参数
            print('init')
        def print_file(self):
            print('age = '+str(self.age))
    stu = Student() #构造函数在实例化时自动调用
    stu.__init__()  
    #构造函数也可以调用,跟普通函数类似,但是不推荐这样用
    但是:对于构造函数 只允许返回None,返回其他则报错

为构造函数添加参数:
    def __init__(self,param1,param2):
    此时实例化类时,必须传入两个值:stu = Student('a','b')
构造函数通常的用法:
    来修改类的数据特征,即重置类的成员变量
        class Student():
            name = ''   
            age = 0
            def __init__(self, name, age): # 至少需要添加self参数
                name = name
                age = age
    上面的代码不报错,但是不能修改name和age的值,并不是因为变量作用域的问题
注意:
    类的变量的作用域 与 模块变量的作用域 完全不同!
    要注意区别类的行为和模块的行为
类变量 实例变量:
代码示例:
    class Student():
        name = ''       name 是类变量:与类相关
        age = 0         age  是类变量:与类相关
        def __init__(self, name, age):  
            self.name = name    self.name 实例变量:与对象相关
            self.age = age      self.age  实例变量:与对象相关

    s1 = Student('a',1) 作为实例变量传入
    s2 = Student('b',8) 作为实例变量传入

注意上述self可以换成任意名称:
    def __init__(this, name, age):  
        this.name = name  
        this.age = age  
    换成this也是对的,但是推荐使用默认的 self

使用区别:
    对象名.成员变量   取决于实例化时的构造 
    类名.成员变量     只跟类有关,不可改变
    应用场景:
        比如定义一个狗类叫做ClassA:
        里面有成员变量 动物种类、狗品种、狗毛色
        有构造函数,参数为品种、毛色,但动物种类变量就等于“狗”,构造时不修改
        实例化时借助构造函数,得到N个不同的狗对象ObjN,可以对应现实世界中不同的狗个体
        此时,ObjN.品种,就是此狗对象的对象属性
        而ClassA.动物类型,表明此类的特征属性,表示共同特性或者不属于个体特性的变量就可以作为类的成员变量

(类的机制)

类变量和实例变量的特性
示例代码:
    class Student():
        name = '类变量name'   
        age = 0
        def __init__(self, name, age): # 至少需要添加self参数
            name = name
            age = age
    obj = Student('实例变量name','实例变量age')
    print(obj.name)         #打印类变量name
    print(Student.name)     #打印类变量name
    print(obj.__dict__)     #打印{}
    print(Student.__dict__) #打印{'name': '类变量name', '__doc__': None, '__weakref__': <attribute '__weakref__' of 'Student' objects>, '__init__': <function Student.__init__ at 0x00000000006BFA60>, '__module__': '__main__', '__dict__':<attribute '__dict__' of 'Student' objects>, 'age': 0}

寻找相关变量的机制:
    如果尝试去访问对象的一个成员变量
    首先会在对象的变量列表obj.__dict__里去查找有没有
    否则,到类的变量列表Student.__dict__去寻找
    否则继续去类的父类中去寻找 

示例代码:
    class Student():
        name = '类变量name'   
        age = 0
        def __init__(self, name, age): # 至少需要添加self参数
            self.name = name
            self.age = age
    obj = Student('实例变量name','实例变量age')
    print(obj.name)         #打印类变量name
    print(Student.name)     #打印实例变量name
    print(obj.__dict__)     #打印{'age': '实例变量age', 'name': '实例变量name'}
    解释:
        修改为self.name之后,则是实例的变量,在构造函数中必须赋值给实例的变量
        定义实例方法例如构造函数时,需要self出现,但是调用实例方法时不需要出现self
    注意:
        self和实例、对象绑定,与类无关
        实例、对象可以调用的方法叫:实例方法,参数第一个必须为 self保留
        self可以换成其他名字,比如this,但位置必须是第一参数
        解释:
            意思是实例方法第一个参数应该保留,具体叫self还是this或其他无所谓,但推荐用 self
Python的类
--- 变量  --- 类变量
          --- 实例变量
--- 方法  --- 实例方法    self + .操作符 -> 修改实例变量
          --- 类方法
          --- 静态方法
--- 构造函数(特殊的实例方法,只是默认调用)

实例方法要操作变量:

    --- 实例方法    self + .操作符 -> 修改实例变量
        例如:
            class Student():
                name = '类变量name'   
                age = 0
                def __init__(self, name1, age): # 至少需要添加self参数
                    self.name = name1
                    self.age = age
                    print(self.name)    #访问的实例变量
                    print(name)             
                    #这也是访问的实例变量,但是访问的是形参name,如果形参不是name,那就会报错
                    #print(__dict__) 
            obj = Student('实例变量name','实例变量age')
            打印:
                实例变量name
                实例变量name

        注意:
            查找变量列表__dict__只能在外部调用时访问,在实例方法内无法打印
            实例方法中,方法参数不要和类变量名相同
            类变量定义时,不要与类内置变量重名

    --- 实例方法    修改类变量
        例如:
            class Student():
                name = '类变量name'   
                age = 0
                def __init__(self, name1, age): # 至少需要添加self参数
                    print('形参name1:'+name1)
                    self.name = name1
                    print('修改实例变量:'+self.name)
                    print('访问类变量法一:'+Student.name) 
                    print('访问类变量法二:'+self.__class__.name)  #注意self.__class__的使用
            obj = Student('实例变量name','实例变量age')
        输出:
            形参name1:实例变量name
            修改实例变量:实例变量name
            访问类变量法一:类变量name
            访问类变量法二:类变量name

    --- 实例方法操作类变量 完成类变量的变化
        class Student():
            sum = 0
            name = '类变量name'   
            age = 0
            def __init__(self, name1, age): # 至少需要添加self参数
                self.__class__.sum += 1
                print('类变量sum变为:'+str(self.__class__.sum)) 
        obj1 = Student('Tom',13)
        obj2 = Student('Kimmy',24)
        obj3 = Student('Jack',18)
        输出:
            类变量sum变为:1
            类变量sum变为:2
            类变量sum变为:3   

        注意:
            实例方法通常是操作实例变量的,但是也可以操作类变量,引出:专门操作类变量的方法
类方法:
定义规范:
    @classmethod        #使用装饰器@classmethod来定义一个类方法
    def plus_sum(cls):  #类方法的参数必须含一个cls参数
        pass

重新完成上述类变量的修改:
    class Student():
        sum = 0
        def __init__(self,param): 
            pass
        @classmethod
        def plus_sum(cls): # cls仍然可以改成别的名字,不建议更改
            cls.sum += 1
            print(cls.sum)
类方法的调用
    stu = Student(1)
    stu.plus_sum()      # 打印 1
    stu = Student(2)
    stu.plus_sum()      # 打印 2
    stu = Student(3)
    stu.plus_sum()      # 打印 3

再次强调:
    实例方法关联的是对象,类方法关联的是类本身
    另外,两者有时候都可以完成参数修改,但是要是操作有“意义”有时就需要区分类方法和实例方法,例如与对象无关的操作就应该使用类方法
    即,对象最好不要调用@classmethod 类方法(虽然不报错,但是缺失实际意义)
静态方法
定义规范:
    @staticmethod
    def add(x,y):
        print("这是一个静态方法")
与其他方法的区别:
    静态方法中没有强制参数
    实例方法中,self 参数代表对象本身
    类方法中 cls 代表类本身
    一个对象或以各类都可以调用静态方法
示例:   
    class Student():
        sum = 0
        name ='Lei'
        def __init__(self,param): 
            self.name = param

        @classmethod
        def plus_sum(cls): 
            cls.sum += 1
            print(cls.sum)
            #print(self.name)    # 类方法不可以引用实例变量
        @staticmethod
        def add(x,y): 
            #print(self.name)            # 静态方法不可以引用实例变量
            print(Student.sum)              # 静态方法可以访问类变量
            print('这是一个静态方法')        
    #调用
    stu = Student(1)
    stu.add(1,1)        # 对象调用静态方法  且访问了类变量 不可以引用实例变量
    Student.add(1,1)    # 类调用静态方法   且访问了类变量 不可以引用实例变量
    stu.plus_sum()      # 对象调用类方法   且访问了类变量 不可以引用实例变量   
    Student.plus_sum()  # 类调用类方法   且访问了类变量 不可以引用实例变量

注意:
    静态方法不要经常使用,与类的关联性不强,与普通函数无区别
类成员的可见性
对于下面示例:
    class Student():
        sum = 0
        def __init__(self,param,param1): 
            self.name = param
            self.age = param1
            self.score = '0'
            print('初值为:'+self.score)

        def marking(self,score): 
            self.score = score
            print('修改后:'+str(score))

        def do_hmwork(self): 
            pass
        def do_eng_hmwork(self): 
            pass

    s = Student(1,2)    # 将socre参数隐藏,不暴露score的直接赋值
    s.score = -1        
    #不推荐方式,直接修改参数,这样没法进行相关过滤,不应该通过直接访问的方式修改
    print('修改后:'+str(s.score))

    #正确方法:所有访问应该通过方法操作变量,可以在方法中对输入进行判断,进而保护数据
    s1 = Student(1,2)
    s1.marking(-1)
注意:
    上述marking方法之外,仍然可以通过 s.score = -1 来直接赋值,
原因:
    上述变量和方法全部都是公开的

Python控制变量的可见性(读、写): 公开public 私有private

    方式:
        私有变量:__私有变量名
        私有函数:__marking()
    注意:
        对于构造函数,因为__init__右边也有下划线,这样不会被识别为私有

    示例:
        class Student():
            sum = 0
            def __init__(self,param,param1): 
                self.name = param
                self.age = param1
                self.__score = '0'
                print('初值为:'+self.__score)

            def __marking(self,score): #添加双下划线
                self.__score = score
                print('修改后:'+str(self.__score))
        s1 = Student(1,2)
        #s1.__marking(-1)      # 访问私有方法报错:'Student' object has no attribute '__marking'
        s1.__score = -1
        print(s1.__score)   # 访问私有变量,成功修改
        #上述原因是:实际是利用py得动态属性,通过点的方式新添加了一个__score变量,原有私有变量并没有修改
        #下面直接访问会发现 访问报错 :'Student' object has no attribute '__score'
        s2 = Student(1,2)
        print(s2.__score) 

    可以利用__dict__内容验证:
        print(s1.__dict__)
        输出:
            {'name': 1, '_Student__score': '0', 'age': 2,'_score':-1}
        print(s2.__dict__)
        输出:
            {'name': 1, '_Student__score': '0', 'age': 2}
        分析上述发现:
            其实私有变量会被改名,此处由__score变为了_Student__score,所以访问原名是访问不到的
            比较两次打印,会发现s1.__score = -1 这句话其实会添加一个__score变量,而没有修改原来的score。因为原来的socre已经被改名了
        上述发现:
            其实Python没有完善的私有变量机制,其仅仅是通过改名,如果使用_Student__score来操作,仍然可以完成修改
面向对象的特性:继承
三大特性:继承、封装、多态
封装:类就是从现实世界的角度对变量和方法进行封装,很抽象比较难讲清楚

类的组成:变量和方法
继承作用:避免定义重复的方法和重复的变量

推荐一个模块创建一个类

对于以下示例:
    c2模块的Human代码如下:
        class Human():
            sum = 0
            def __init__(self, name, age):
                self.name = name
                self.age = age
            def get_name(self):
                print(self.name)

    子类Student如下:
        from c2 import Human
        class Student(Human):  # 标准的继承方法
            sum = 0
            def __init__(self,name,age): 
                self.name = name
                self.age = age
                self.__score = 0
                print('初值为:'+str(self.__score))

        s = Student('Tom',13)  # 实例化子类时,要按照父类的构造函数传参
        print(s.sum)
        print(Student.sum)  # 打印 0 表示子类继承了父类的类变量
        print(s.name)   # 打印 Tom 表示子类继承了父类的实例变量
        print(s.age)    # 打印 13 表示子类继承了父类的实例变量
        s.get_name()   # 打印 Tom 表示子类继承了父类的实例方法
    注意:
        上述只是将Human父类的变量和方法提取到了子类中
        Python允许多继承,一个子类可以有多个父类,一般用不到
    进一步:
        现在子类有自己独有的方法和变量
        例如:Student类有school变量,那么其构造函数为school+父类构造参数


    在子类里调用父类的函数,示例:

        from c2 import Human
        class Student(Human):  # 标准的继承方法
            def __init__(self,school,name,age): # 父类构造参数是name age
                self.school = school
                Human.__init__(self,name,age)   #直接调用父类构造函数 传参
                #注意此处父类构造参数要加上self,此处是!普通函数的调用!,传参缺一不可,self必不可少
        s = Student('YangTz','Tom',13)
        print(s.school) # 正确打印YangTz
        print(s.name) # 正确打印Tom
        print(s.age)# 正确打印13

    开闭原则:
        对扩展是开放的,对更改本身是关闭的
    注意:
        Human.__init__(self,name,age)
        上述使用类,调用了实例方法,其实不推荐这样做,如果类调用一个实例方法,那么实例方法的 self 参数会成为一个普通参数,调用时应该被传入方法内
        现在对于上述代码,如果父类改变,那么代码中涉及的地方全都要改,违反了开闭原则

    引出:super() 通用调用方法,修改为:
        super(Student,self).__init__(name,age)
    注意:
        这样修改父类时不需要修改这里的代码
        super()目的是继承父类的同名方法,如__init__()或一些公共方法
        对于一个普通实例方法do_something(self),如果其和父类方法同名,那么会优先调用子类的此方法
        但是如果修改为
            def do_homework(self):
                super(Student,self).do_homework()
        那么此时表示子类的该实例方法继承了父类的该方法,此时调用会执行父类的do_something()
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2017-10-23,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 面向对象
  • 方法 和 函数:
  • 类变量 实例变量:
  • 类变量和实例变量的特性
  • Python的类
  • 类方法:
  • 静态方法
  • 类成员的可见性
  • 面向对象的特性:继承
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档