zoukankan      html  css  js  c++  java
  • Python面向对象基础:设置对象属性

    用类存储数据

    类实际上就是一个数据结构,对于python而言,它是一个类似于字典的结构。当根据类创建了对象之后,这个对象就有了一个数据结构,包含一些赋值了的属性。在这一点上,它和其它语言的struct的作用是类似的:存储数据并提供数据检索功能

    例如,下面是史上最简单的类:

    class Person: pass
    

    pass关键字表示这个类没有实际的逻辑体。这里只是定义了一个类,这个类的对象初始化时不会存放任何数据。现在,构造一个对象,让它和dict一样存放一些数据:

    p = Person()    # 构造对象
    p.name = "longshuai"  # 创建对象的属性name
    p.age = 23            # 创建对象的属性age
    

    现在,Person的实例对象p中就存放了两个属性:p.name和p.age。可以直接去检索存放在p中的数据:

    print(p.name)  # 输出"longshuai"
    print(p.age)   # 输出23
    

    也可以使用dict来存储这些数据:

    >>> d={}
    >>> d["name"]="longshuai"
    >>> d["age"]=23
    
    >>> print(d["name"])
    longshuai
    >>> print(d["age"])
    23
    

    在数据存储方面,它们的作用是完全等价的。实际上对象/类在内部就是使用一个名为__dict__的dict类型来存放它所拥有的数据的。

    >>> p.__dict__
    {'name': 'longshuai', 'age': 23}
    

    __init__()构造对象初始数据

    上面的name和age属性是在构建了对象之后附加上去的,如果想要创建对象时就存放好数据,可以定义类的构造函数__init__()。例如:

    class Person:
        def __init__(self,name,age):
            self.name = name
            self.age = age
    

    然后创建对象的时候,传递name参数和age参数即可。

    >>> p = Person("longshuai",23)
    >>> p.__dict__
    {'name': 'longshuai', 'age': 23}
    

    如果想定义这个类公有的数据,可以将公有属性定义为类的属性。比如中国人都是黄皮肤:

    class Person:
        skin = "yellow"
        def __init__(self,name,age):
            self.name = name
            self.age = age
    

    这样每次创建Person的对象实例时,每个对象都会有相同的肤色:yellow。但注意,这个skin属性是类属性,不是对象属性,它是存放在类的名称空间中的。当对象真的需要这个属性的时候,会临时去检索类的名称空间来获取。看下面的__dict__字典即可知道:

    >>> p = Person("longshuai",23)
    >>> p.__dict__
    {'name': 'longshuai', 'age': 23}
    >>> p.skin
    'yellow'
    

    但注意,按照面向对象的封装原则,在类中定义类变量属性是不合理的,因为要在外部访问它需要通过x.y的方式,这意味着打开了封装好的"黑匣子",暴露了属性。除非真的有需要,否则可以将类变量的定义放进构造函数__init__()中,这样每个初始化的对象都会有该属性。

    setter和getter方法

    在面向对象的角度上考虑,一般是不建议直接在类的外部通过x.name的方式赋值、取值的。而是定义对应的方法,通过方法来取得对应的值。这两类方法称为setter、getter方法:setter用于赋值或设置属性值,getter用于取得属性值。

    class Person:
        skin = "yellow"
        def __init__(self,name,age):
            self.name = name
            self.age = age
        def set(self,job):
            self.job = job
            return self
        def get(self):
            return self.name,self.age,self.job
    

    上面的set方法用于设置一个新属性job。get用于返回对象的3个属性。

    >>> p = Person("longshuai",23)
    >>> p.set("manager")
    >>> name, age, job = p.get()
    >>> print([name,age,job])
    ['longshuai', 23, 'manager']
    

    需要注意,setter方法可以有多种类型的返回值,常用的有4种:

    1. 返回设置后的值
    2. 返回设置前的值
    3. 返回对象自身
    4. 返回布尔值,表示是否设置成功

    这4种返回值都很常见,特别是第三种用来串联对象方法的时候非常好用。修改上面的set方法:

    class Person:
        skin = "yellow"
        def __init__(self,name,age):
            self.name = name
            self.age = age
        def set(self,job):
            self.job = job
            return self
        def get(self):
            return self.name,self.age,self.job
    

    上面的set()返回self对象自身。于是串联set()和get():

    >>> p = Person("longshuai",23)
    >>> name,age,job = p.set("manager").get()
    

    无论使用何种返回值方式,都不会真正影响程序的使用。但使用合理的返回值类型,可能会简化代码的编写。另外,决定了返回值的方式后,就不要再去修改,因为很可能会牵一发而动全身。

    上面的getter返回了多个值,但一般来说getter只返回一个对应的属性。比如getname()返回name属性,getage()返回age属性等。这样需要定义多个getter方法。

    def get_name(self):
        return self.name
    
    def get_age(self):
        return self.age
    
    def get_job(self):
        return self.job
    

    合并setter和getter

    很多时候可以合并setter和getter方法。合并的方式是判断方法的参数,如果调用方法的时候给了参数,就表示setter,没有给定参数,就表示是getter。

    例如,对于job属性:

    def set_get_job(self, job=None):
        if job:
            self.job = job
        else:
            return self.job
    

    现在可以以给参数和不给参数两种不同的方式来调用set_get_job()方法:

    p = Person("longshuai", 23)
    p.set_get_job("manager")     # 给了参数,说明是setter
    job = p.set_get_job()        # 没给参数,说明是getter
    

    python的属性管理

    上面解释了各种setter、getter的方式,还解释了将它们进行合并。

    实际上在python中访问、设置、删除对象属性的时候,大概有以下几种方式:

    1. 使用内置函数getattr()、setattr()和delattr()
    2. 自己编写getter()setter()deleter()方法
    3. 重载__getattr__()__setattr__()__delattr__()运算符,这决定了x.y的访问、赋值方式以及del x.y的方式
    4. 使用__getattribute__()方法
    5. 使用描述符协议
    6. 使用property协议,它是一种特殊的描述符

    这些还未介绍到的属性管理方式,在后面的文章中会逐渐展开解释。

    总结

    本文介绍了各种设置对象属性的方式,属性其实就是数据,对象/类就是属性的容器,这一点很重要。我最开始学java的面向对象时,虽然对类和对象有那些教科书式的理解,但始终没有感受到类/对象其实就是一种用来存储数据的数据结构。直到学习了Python/Perl,我才意识到这一点,然后理解面向对象就容易的多了。

  • 相关阅读:
    用归并排序或树状数组求逆序对数量 poj2299
    UVA10600 次小生成树
    最小生成树求最大比率 UVALive
    求树的重心 poj 1655
    求树的直径+并查集(bfs,dfs都可以)hdu4514
    树形DP+RMQ+尺取法 hdu4123
    区间dp 51nod1021
    LCS poj1080
    最长公共子序列hdu1503
    python No handlers could be found for logger错误的解决
  • 原文地址:https://www.cnblogs.com/f-ck-need-u/p/10091181.html
Copyright © 2011-2022 走看看