进阶学Python:Python面向对象之属性方法

Posted 编程界的小胖子

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了进阶学Python:Python面向对象之属性方法相关的知识,希望对你有一定的参考价值。

这次我们主要介绍类中定义的各种属性,例如类属性、实例属性、类的私有属性,以及各种方法,例如实例方法、类方法、静态方法以及property 属性方法等相关知识。


🚀看一段代码

class Tool(object):
    dog_type = "Huskie"  # 类属性

    def __init__(self,name):
        self.name = name  # 实例属性
    
    # 实例方法
    def tell_info(self):
        pass
    
    # 类方法
    @classmethod
    def bar(cls): 
        pass
    
    # 静态方法
    @staticmethod
    def foo(x):
        pass
    
    #属性方法
    @property
    def tell_name(self):
        return self.name
复制代码

类属性和实例属性

无论是类属性还是实例属性都是定义在类中,但是其根本区别是保存的位置和调用的对象不相同。例如:

#!/usr/bin/env python
# -*- encoding: utf-8 -*-
'''
@File    :   dog.py
@Time    :   2019/10/23 15:00:58
@Author  :   YongJia Chen 
@Version :   1.0
@Contact :   chen867647213@163.com
@License :   (C)Copyright 2018-2019, Liugroup-NLPR-CASIA
@Desc    :   None
'''

# here put the import lib


class Dogs(object):
    # 类属性
    belongTo = "Animals"

    def __init__(self, name):
        #实例属性
        self.name = name


dog1 = Dogs("Sunny Chen")
print(Dogs.belongTo)  # 类属性通过类访问
print(dog1.name)  # 实例属性通过实例访问
print(dog1.belongTo)  #类属性也可以被实例访问
# print(Dogs.name)  # 但是实例属性无法被类访问
复制代码

类属性可以被类和属性访问,而实例属性只能被实例访问,这是由于:

这是由于每通过一个类创建一个实例对象,则会开辟一个内存空间,用来存放该实例对象的属性和方法,以及类对象的指针,实例对象之所以可以调用类中的方法,是因为可以通过类对象指针访问类的属性和方法。

但是若实例对象想修改类的属性和方法,则需要通过特殊的方法。例如:

#!/usr/bin/env python
# -*- encoding: utf-8 -*-
'''
@File    :   dog.py
@Time    :   2019/10/23 15:03:56
@Author  :   YongJia Chen 
@Version :   1.0
@Contact :   chen867647213@163.com
@License :   (C)Copyright 2018-2019, Liugroup-NLPR-CASIA
@Desc    :   None
'''

# here put the import lib


class Dogs(object):
    # 类属性
    belongTo = "Animals"

    def __init__(self, name):
        #实例属性
        self.name = name


dog1 = Dogs("Sunny Chen")

dog1.belongTo = "wakka"
print(Dogs.belongTo)  #Animals
# 注:通过这种方式是无法修改类属性的,而是在实例的属性字典中重写了该属性
print(dog1.__dict__)  #{'name': 'Sunny Chen', 'belongTo': 'wakka'}

dog1.__class__.belongTo = "Wakka"
print(Dogs.belongTo)  # Wakka--修改成功
复制代码

实例方法、类方法和静态方法

实例方法: 通常由对象调用,必须传入实例对象,执行实例方法时,自动将调用该方法的实例对象本身传给该方法的self参数。

类方法: 通常由类调用,必须传入类对象本身,执行类方法时,自动将调用该方法的类对象赋值给cls参数;

静态方法: 类和实例对象均可调用,不传实例对象和类对象,无默认参数。

#!/usr/bin/env python
# -*- encoding: utf-8 -*-
'''
@File    :   testDemo.py
@Time    :   2019/10/23 15:09:21
@Author  :   YongJia Chen 
@Version :   1.0
@Contact :   chen867647213@163.com
@License :   (C)Copyright 2018-2019, Liugroup-NLPR-CASIA
@Desc    :   None
'''

# here put the import lib


class Test(object):
    def __init__(self, name):
        self.name = name

    #实例方法
    def mm(self):
        print("in the mm")

    #类方法
    @classmethod
    def tt(cls):
        print("in the tt")
        print(cls)

    #静态方法
    @staticmethod
    def gg(a):
        print("in the gg", a)


test1 = Test("alex")
test1.mm()
test1.tt()  # 实例可以调用类方法,则默认将该类对象传入 --》<class '__main__.Test'>
test1.gg(100)

Test.mm(test1)  # 类对象调用实例方法必须传入一个类
Test.tt()
Test.gg(100)
复制代码

相同点:对于所有的方法而言,均属于类(非对象)中,所以,在内存中也只保存一份。

不同点:方法调用者不同、调用方法时自动传入的参数不同。

类方法和静态方法的作用:

类方法:主要使用类方法来管理类属性,无论是私有属性还是普通类属性,在很多类中,可能不需要实例化,仅仅是为了封装,这时候我们就可以通过类方法来管理类属性。 

静态方法:当我们有许多杂乱且无联系的函数时,我们需要将函数封装在类中,而无法修改函数的代码即参数,仅仅是为了将这些函数通过类的方式封装起来,方便进行管理。例如:

#!/usr/bin/env python
# -*- encoding: utf-8 -*-
'''
@File    :   toolDemo2.py
@Time    :   2019/10/23 15:11:13
@Author  :   YongJia Chen 
@Version :   1.0
@Contact :   chen867647213@163.com
@License :   (C)Copyright 2018-2019, Liugroup-NLPR-CASIA
@Desc    :   None
'''

# here put the import lib


class Tool(object):
    """
    定义一个工具箱,用来管理这些工具,仅仅是为了封装而不影响这些函数,方便管理
    """
    @staticmethod
    def hammer():  # 锤子
        pass

    @staticmethod
    def ax():  # 斧头
        pass

    @staticmethod
    def wrench():  # 扳手
        pass


# 当我们需要使用这些工具是,直接用过类对象调用即可
# 同时不会传入额外的参数,例如self-实例本身,cls -类本身,仅仅是为了达到封装的效果
Tool.hammer()
Tool.ax()
复制代码

属性方法 property

字面意思就是 使方法变为一种特殊的属性,即一种用起来像是使用的实例属性一样的特殊属性,可以对应于某个方法。例如:

#!/usr/bin/env python
# -*- encoding: utf-8 -*-
'''
@File    :   testDemo.py
@Time    :   2019/10/23 15:13:21
@Author  :   YongJia Chen 
@Version :   1.0
@Contact :   chen867647213@163.com
@License :   (C)Copyright 2018-2019, Liugroup-NLPR-CASIA
@Desc    :   None
'''

# here put the import lib


class Test(object):
    def foo(self):
        print("in the fo")

    # 定义property属性
    @property
    def bar(self):
        # print("in the bar")
        return "in the bar"


tt = Test()
tt.foo()  # 调用普通实例方法  -- in the fo
ret = tt.bar  # 调用方式与类属性一样,并且也有返回值  -- in the bar
print(ret)
复制代码

定义时,在实例方法上加上@property装饰器,同时有且仅有一个self参数;

调用时,与调用类属性一样,无括号

# 对于京东商城中显示电脑主机的列表页面,每次请求不可能把数据库中的所有内容都显示到页面上,而是通过分页的功能局部显示,所以在向数据库中请求数据时就要显示的指定获取从第m条到第n条的所有数据 这个分页的功能包括:

# 根据用户请求的当前页和总数据条数计算出 m 和 n
# 根据m 和 n 去数据库中请求数据


class Pager:
    def __init__(self, current_page):
        # 用户当前请求的页码(第一页、第二页...)
        self.current_page = current_page
        # 每页默认显示10条数据
        self.per_items = 10

    @property
    def start(self):
        val = (self.current_page - 1) * self.per_items
        return val

    @property
    def end(self):
        val = self.current_page * self.per_items
        return val


# 调用
p = Pager(1)
p.start  # 就是起始值,即:m
p.end  # 就是结束值,即:n
复制代码

总结:@property,将方法转化为一种特殊的属性的装饰器,故既然既然是属性,自然会有增删改查(调用、修改、删除)等,那么我们来看怎么实现property属性的增删改查。


property 属性的增删改查(两种方式):

两种方式 :

  1. 具有三种 @property 装饰器 ;
  2. 类属性方式,创建值为 property对象 的类属性

三种@property装饰器

#!/usr/bin/env python
# -*- encoding: utf-8 -*-
'''
@File    :   goodDemo.py
@Time    :   2019/10/23 15:17:57
@Author  :   YongJia Chen 
@Version :   1.0
@Contact :   chen867647213@163.com
@License :   (C)Copyright 2018-2019, Liugroup-NLPR-CASIA
@Desc    :   None
'''

# here put the import lib

# 方式一:通过装饰器,当执行某操作时,自动触发装饰器及装饰器下方法的执行(copy)

class Goods(object):
    def __init__(self):
        # 原价
        self.original_price = 100
        # 折扣
        self.discount = 0.8

    @property
    def price(self):
        # 实际价格 = 原价 * 折扣
        new_price = self.original_price * self.discount
        return new_price

    @price.setter  # func_name.setter
    def price(self, value):
        self.original_price = value

    @price.deleter  # func_name.deleter
    def price(self, value):
        del self.original_price


obj = Goods()
obj.price  # 获取商品价格
obj.price = 200  # 修改商品原价
del obj.price  # 删除商品原价

# 解析:有点类似于魔法,当执行某操作时,自动触发装饰器的运行
#   @property -- 当获取price属性时,自动执行 @property 修饰的方法,并获取方法的返回值,例如:obj.func
#    @func.setter --当设置修改price属性时,自动执行 @func.setter 修饰的 func 方法,并将值赋值给方法的参数,例如:obj.func = 200
#    @func.deleter --当删除price属性时,自动执行 @func.deleter 修饰的func 方法
复制代码

类属性方式,创建值为 property对象 的类属性

#!/usr/bin/env python
# -*- encoding: utf-8 -*-
'''
@File    :   fooDemo.py
@Time    :   2019/10/23 15:20:30
@Author  :   YongJia Chen 
@Version :   1.0
@Contact :   chen867647213@163.com
@License :   (C)Copyright 2018-2019, Liugroup-NLPR-CASIA
@Desc    :   None
'''

# here put the import lib


class Foo(object):
    def get_bar(self):
        num = 100
        return num

    def set_bar(self, value):
        """必须两个参数"""
        num = value
        return num

    def del_bar(self):
        num = 100
        del num
        return "deleted this attribute"

    #顺序不能错,分别为 获取、修改、删除、描述
    BAR = property(get_bar, set_bar, del_bar, "description...")


obj = Foo()

obj.BAR  # 自动调用第一个参数中定义的方法:get_bar
obj.BAR = 200  # 自动调用第二个参数中定义的方法:set_bar方法,并将200当作参数传入
del obj.BAR  # 自动调用第三个参数中定义的方法:del_bar方法
desc = Foo.BAR.__doc__  # 自动获取第四个参数中设置的值:description...
print(desc)
print(obj.BAR)
复制代码

关于property属性的使用很常见,优势十分明显:

为用户提供一个简单的属性,而不再是复杂的函数(不同考虑函数的参数是什么、函数的返回值是什么),仅仅以调用属性的方式得到函数的返回值。本质上也是封装的体现即封装底层复杂的实现方式,给用户提供一个简单、使用的属性接口。以下是大公司的代码:

#!/usr/bin/env python
# -*- encoding: utf-8 -*-
'''
@File    :   messageDemo.py 腾讯即时通信模块-参考
@Time    :   2019/10/23 15:28:04
@Author  :   YongJia Chen 
@Version :   1.0
@Contact :   chen867647213@163.com
@License :   (C)Copyright 2018-2019, Liugroup-NLPR-CASIA
@Desc    :   None
'''

# here put the import lib

# coding: utf-8

import random
import time


class Message(object):
    def __init__(self, msgarr=[], toacc=''):
        self.msgbody = msgarr  # 此处为MsgDict对象实例的列表或者空列表
        self.toacc = toacc  # toacc为字符串(单发)或者列表(批量发)
        self.msgrandom = random.randint(1, 1000000000)
        self.msgrequest = {
            'To_Account': toacc,  # 消息接收方账号
            'MsgRandom': self.msgrandom,  # 消息随机数,由随机函数产生
            'MsgBody': [t.msg for t in msgarr]
        }

    def del_option(self, option):
        if option in (set(self.msgrequest) -
                      set(['To_Account', 'MsgRandom', 'MsgBody'])):
            self.__dict__.pop(option)
            self.msgrequest.pop(option)

    def append_msg(self, msg):
        self.msgbody.append(msg)
        self.msgrequest['MsgBody'].append(msg.msg)

    def insert_msg(self, index, msg):
        self.msgbody.insert(index, msg)
        self.msgrequest['MsgBody'].insert(msg.msg)

    def del_msg(self, index):
        if index in range(len(self.msgbody)):
            del self.msgbody[index]
            del self.msgrequest['MsgBody'][index]

    def set_from(self, fromacc):
        # 指定消息的发送方,默认为服务器发送
        self.fromacc = fromacc
        self.msgrequest['From_Account'] = fromacc

    def set_to(self, toacc):
        # 指定消息的接收方,可以为String(单发),可以为List(批量发送)
        self.toacc = toacc
        self.msgrequest['To_Account'] = toacc

    def refresh_random(self):
        self.msgrandom = random.randint(1, 1000000000)
        self.msgrequest['MsgRandom'] = self.msgrandom,  # 消息随机数,由随机函数产生

    def set_sync(self, sync):
        # 同步选项:1, 把消息同步到From_Account在线终端和漫游上
        #           2, 消息不同步至From_Account
        #           若不填写,默认情况下会将消息同步
        #           仅在单发单聊消息中可调用
        self.sync = sync
        self.msgrequest['SyncOtherMachine'] = sync

    def set_timestamp(self):
        # 设置消息时间戳,unix时间, 仅在单发单聊消息中可以调用
        self.timestamp = int(time.time())
        self.msgrequest['MsgTimeStamp'] = self.timestamp

    def set_offlinepush(self, pushflag=0, desc='', ext='', sound=''):
        # 仅适用于APNa推送,不适用于安卓厂商推送
        self.msgrequest['OfflinePushInfo'] = {
            'PushFlag': pushflag,
            'Desc': desc,
            'Ext': ext,
            'Sound': sound
        }


class MsgDict(object):
    def __init__(self, msgtype='', msgcontent={}):
        self.msgtype = msgtype
        self.msgcontent = msgcontent

    @property
    def msg(self):
        return {'MsgType': self.msgtype, 'MsgContent': self.msgcontent}

    def set_content(self, content):
        self.msgcontent = content


class TextMsg(MsgDict):
    def __init__(self, text='', msgtype='TIMTextElem'):
        self.text = text
        content = {'Text': text}
        super(TextMsg, self).__init__(msgtype=msgtype, msgcontent=content)

    def set_text(self, text):
        self.text = text
        self.msgcontent['Text'] = text


class LocationMsg(MsgDict):
    def __init__(self,
                 desc='',
                 latitude=0,
                 longitude=0,
                 msgtype='TIMLocationElem'):
        self.desc = desc
        self.latitude = latitude
        self.longitude = longitude
        content = {
            'Desc': desc,  # 地理位置描述信息, String
            'Latitude': latitude,  # 纬度, Number
            'Longitude': longitude  # 经度, Number
        }
        super(LocationMsg, self).__init__(msgtype=msgtype, msgcontent=content)

    def set_desc(self, desc):
        self.desc = desc
        self.msgcontent['Desc'] = desc

    def set_location(self, latitude, longitude):
        self.latitude = latitude
        self.longitude = longitude
        self.msgcontent['Latitude'] = latitude
        self.msgcontent['Longitude'] = longitude

    def set_latitude(self, latitude):
        self.latitude = latitude
        self.msgcontent['Latitude'] = latitude

    def set_longitude(self, longitude):
        self.longitude = longitude
        self.msgcontent['Longitude'] = longitude


class FaceMsg(MsgDict):
    def __init__(self, index=1, data='', msgtype='TIMFaceElem'):
        self.index = index
        self.data = data
        content = {
            'Index': index,  # 表情索引,用户自定义, Number
            'Data': data  # 额外数据, String
        }
        super(TextMsg, self).__init__(msgtype=msgtype, msgcontent=content)

    def set_index(self, index):
        self.index = index
        self.msgcontent['Index'] = index

    def set_data(self, data):
        self.data = data
        self.msgcontent['Data'] = data


class CustomMsg(MsgDict):
    def __init__(self,
                 data='',
                 desc='',
                 ext='',
                 sound='',
                 msgtype='TIMCustomElem'):
        self.data = data
        self.desc = desc
        self.ext = ext
        self.sound = sound
        content = {
            'Data':
            data,  # 自定义消息数据。不作为APNS的payload中字段下发,故从payload中无法获取Data字段, String
            'Desc': desc,  # 自定义消息描述,当接收方为iphone后台在线时,做ios离线Push时文本展示
            'Ext':
            ext,  # 扩展字段,当接收方为ios系统且应用处在后台时,此字段作为APNS请求包Payloads中的ext键值下发,Ext的协议格式由业务方确定,APNS只做透传
            'Sound': sound  # 自定义APNS推送铃声
        }
        super(CustomMsg, self).__init__(msgtype=msgtype, msgcontent=content)

    def set_data(self, data):
        self.data = data
        self.msgcontent['Data'] = data

    def set_desc(self, desc):
        self.desc = desc
        self.msgcontent['Desc'] = desc

    def set_ext(self, ext):
        self.ext = ext
        self.msgcontent['Ext'] = ext

    def set_sound(self, sound):
        self.sound = sound
        self.msgcontent['Sound'] = sound


class SoundMsg(MsgDict):
    def __init__(self, uuid='', size=0, second=0, msgtype='TIMSoundElem'):
        self.uuid = uuid
        self.size = size
        self.second = second
        content = {
            'UUID': uuid,  # 语音序列号,后台用于索引语音的键值,String
            'Size': size,  # 语音数据大小, Number 
            'Second': second  # 语音时长,单位秒 Number 
        }
        super(SoundMsg, self).__init__(msgtype=msgtype, msgcontent=content)

    def set_uuid(self, uuid):
        self.uuid = uuid
        self.msgcontent['UUID'] = uuid

    def set_size(self, size):
        self.size = size
        self.msgcontent['Size'] = size

    def set_second(self, second):
        self.second = second
        self.msgcontent['Second'] = second


class ImageMsg(MsgDict):
    def __init__(self,
                 uuid='',
                 imgformat=0,
                 imginfo=[],
                 msgtype='TIMImageElem'):
        self.uuid = uuid
        self.imgformat = imgformat
        self.imginfo = imginfo
        content = {
            'UUID': uuid,  # 图片序列号,后台用于索引语音的键值,String
            'ImageFormat':
            imgformat,  # 图片格式, BMP=1, JPG=2, GIF=3, 其他=0, Number 
            'ImageInfoArray':
            [t.info for t in imginfo]  # 原图,缩略图或者大图下载信息, Array
        }
        super(ImageMsg, self).__init__(msgtype=msgtype, msgcontent=content)

    def set_uuid(self, uuid):
        self.uuid = uuid
        self.msgcontent['UUID'] = uuid

    def set_format(self, imgformat):
        self.imgformat = imgformat
        self.msgcontent['ImageFormat'] = imgformat

    def append_info(self, info):
        # info 为ImageInfo对象实例
        self.imginfo.append(info)
        self.msgcontnet['ImageInfoArray'].append(info.info)

    def insert_info(self, index, info):
        self.imginfo.insert(index, info)
        self.msgcontent['ImageInfoArray'].insert(index, info.info)

    def del_info(self, index):
        del self.imginfo[index]
        del self.msgcontent['ImageInfoArray'][index]


class FileMsg(MsgDict):
    def __init__(self, uuid='', size=0, name='', msgtype='TIMFileElem'):
        self.uuid = uuid
        self.size = size
        self.name = name
        content = {
            'UUID': uuid,  # 文件序列号,后台用于索引语音的键值,String
            'FileSize': size,  # 文件数据大小, Number 
            'FileName': name  # 文件名称/路径, String
        }
        super(FileMsg, self).__init__(msgtype=msgtype, msgcontent=content)

    def set_uuid(self, uuid):
        self.uuid = uuid
        self.msgcontent['UUID'] = UUID

    def set_size(self, size):
        self.size = size
        self.msgcontent['FileSize'] = size

    def set_name(self, name):
        self.name = name
        self.msgcontent['FileName'] = name


class ImageInfo(object):
    def __init__(self, itype=1, size=0, width=0, height=0, url=''):
        #图片类型, 1-原图, 2-大图, 3-缩略图, 0-其他
        self.itype = itype
        # 图片数据大小,Number
        self.size = size
        # 图片宽度,Number
        self.width = width
        # 图片高度, Number
        self.height = height
        # 图片下载地址,String
        self.url = url

    @property
    def info(self):
        return {
            'Type': self.itype,
            'Size': self.size,
            'Width': self.width,
            'Height': self.height,
            'URL': self.url
        }

    def set_type(self, itype):
        self.itype = itype

    def set_size(self, size):
        self.size = size

    def set_width(self, width):
        self.width = width

    def set_height(self, height):
        self.height = height

    def set_url(self, url):
        self.url = url
复制代码

快去动手试试吧!

 

 ① 2000多本Python电子书(主流和经典的书籍应该都有了)

② Python标准库资料(最全中文版)

③ 项目源码(四五十个有趣且经典的练手项目及源码)

④ Python基础入门、爬虫、web开发、大数据分析方面的视频(适合小白学习)

⑤ Python学习路线图(告别不入流的学习)

需要相关资料的可以通过扫一扫

 

 

以上是关于进阶学Python:Python面向对象之属性方法的主要内容,如果未能解决你的问题,请参考以下文章

Python学习之面向对象进阶

python学习之面向对象学习进阶

python进阶之面向对象初识

Python 面向对象编程之进阶使用

13Python之面向对象进阶篇

Python进阶学习之面向对象