使用property为类中的数据添加行为

对于面向对象编程特别重要的是,关注行为和数据的分离

在这之前,先来讨论一些“坏”的面向对象理论,这些都告诉我们绝不要直接访问属性(如Java):

class Color:
    def __init__(self, rgb_value, name):
        self._rgb_value = rgb_value
        self._name = name

    def set_name(self, name):
        self._name = name

    def get_name(self):
        return self._name

前缀有一个单下划线的变量表明他们是类私有的,接着get和set方法提供了对每个变量的访问方式,这个类在实际使用中一般采用如下的方式:

>>> c = Color(‘#ff0000‘, ‘bright red‘)
>>> c.get_name()
‘bright red‘
>>> c.set_name(‘red‘)
>>> c.get_name()
‘red‘

这并不像python喜欢的直接访问方式具有可读性:

class Color:
    def __init__(self, rgb_value, name):
        self.rgb_value = rgb_value
        self.name = name

调用如下:

>>> c = Color(‘#ff0000‘, ‘bright red‘)
>>> print(c.name)
bright red
>>> c.name = "red"
>>> print(c.name)
red

Java的这种方式方便在需要这些变量被赋值时添加额外的代码,例如我们想要验证输入值是否合理,则可以改变set_name()方法来实现:

def set_name(self, name):
    if not name:
        raise Expception("Invalid Name")
    self._name = name

但是这样会有一个问题,采用直接访问属性方法的代码,现在必须通过调用方法才能访问原有的属性,如果他们不改变自己的访问方式,那么代码就被破坏了。

而在python中可以使用property关键字来处理该问题,加入我们原本使用直接成员访问的方法取访问属性,之后我们可以增加几个方法,在不改变访问接口的情况下,来对name这个变量进行取值和赋值。

class Color:
    def __init__(self, rgb_value, name):
        self.rgb_value = rgb_value
        self._name = name

def _set_name(self, name):
        if not name:
            raise Exception("Invalid Name")
        self._name = name

    def _get_name(self):
        return self._name

    name = property(_get_name, _set_name)

先将name这个属性改为一个(半)私有的_name属性,接着我们添加两个(半)私有方法对这个变量进行取值和赋值,并在赋值的时候进行验证。最后我们在代码底部使用property关键字进行声明。

现在Color类拥有了一个全新的name属性,这个name属性变为了一个property属性,需要通过调用我们刚刚添加的两个方法才能访问或者改变其值。而Color类仍能以前一个版本中相同的方式来使用,同时它还能支持对name赋值时进行验证

>>> c = Color(‘#ff0000‘, ‘bright red‘)
>>> print(c.name)
bright red
>>> c.name = ‘red‘
>>> print(c.name)
red
>>> c.name =""
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "color2.py", line 8, in _set_name
    raise Exception("Invalid Name")
Exception: Invalid Name

这样,我们之前编写的任何代码仍然能够工作,但是,即便name变成了property属性,也不能保证100%的安全,如果有人使它设定为空字符串值,仍然可以通过直接访问_name属性的方式来达到目的。

property是怎样工作的

property函数实际上返回了一个对象,该对象通过我们指定的方法代理了全部对属性值访问或赋值的请求。

property构造函数实际上还可以接受两个额外的参数:一个删除函数和一个property的文本字符串。在实际中很少用到删除函数,但是如果需要用到记录所删除的值,那么删除函数还是很有用的。同时在我们满足某个条件的情况下,删除函数还可以否决删除操作。文本字符串是一个用来描述该property的字符串。如果我们不提供文本字符串这个参数,那么该值将从property的第一个参数,也就是getter方法的文本字符串中复制过来。

参考:

1、《Python3 面向对象编程》 [加]Dusty Philips 著

原文地址:https://www.cnblogs.com/anovana/p/8257728.html

时间: 2024-08-01 07:19:03

使用property为类中的数据添加行为的相关文章

继承类中static数据值

1 class A{ 2 static int num = 1; 3 public static void Display(){ 4 System.out.println( num ); 5 } 6 } 7 8 class B extends A{ 9 static int num = 2; 10 public static void Display(){ 11 System.out.println( num ); 12 } 13 } 14 15 class C extends A{ 16 st

C++类中常量数据成员和静态数据成员初始化

常量数据成员初始化原则: 在每一个构造函数的初始化列表中初始化 静态数据成员初始化原则: 类内声明,类外初始化(因为它是属于类的,不能每构造一个对象就初始化一次) // test_max.cpp : 定义控制台应用程序的入口点. #include "stdafx.h" #include <iostream> #include <vector> using namespace std; class A { public: A(int i):a(0) {} A():

c++类中对数据成员进行初始化和赋值的区别

在c++中定义一个类 ,对于构造函数 我们经常是这么写的: class test { public: test(int n_x , int n_y) { x = n_x; y = n_y; } private: int x , y; }; 这中写法虽然是合法的但比较草率 在构造函数 test(int n_x , int n_y)中 , 我们这样实际上不是对数据成员进行初始化 , 而是进行赋值. 正确的是初始化应该是这样的: class test { public: test() {} test(

在另一个类中做数据成员的对象,可以先不初始化

class A { B b; } 因为在创建A类的时候,会先调用A的构造函数,同时对B类中的b对象调用他的构造函数 下面测试代码 class A { public: int a; A(int x) :a(x){}; }; class B:public A { private: A b; public: B(int x, int y) :A(x), b(y){} void display() { cout << a << endl << b.a << endl

将数据表中的数据添加到ComboBox控件中

实现效果: 知识运用: ComboBox控件的DataSource 属性 //获取或设置ComboBox的数据源 public Object DataResouce{get;set;} //属性值:任何实现了IList接口的对象 如DataSet 泛型集合或数组 DisplayMember属性 //获取或设置要为此列表控件显示的属性 public string DisplayMember{get;set;}  ValueMember属性 //用于列表控件中数据项的实际值 默认为string.Em

.net EF中从数据添加表或视图时无法添加的问题

.net 使用EF模式进行开发,添加实体时不能够正常添加 错误描述: .net中在EF文件中添加数据库中已有的表或视图时不能正常添加,在添加时没有任何的错误提示,但是表或视图就一直拉不过来,,保存也没有相应的表或视图. 解决方案: 出现这种情况的时候要看看表或视图中是否有主键或者不能为空的字段,如果没有类似字段或主键,添加一个这样的字段在试试,一般来说添加一个这样的字段应该就能解决. 我所碰到的问题添加了一个主键就解决.添加一个主键就可以正常的把表或视图添加EF模型中去了.如果配到类似的问题可以

vc中将access数据库表中的数据添加到一个ComboBox中,实现从数据库中添加ComboBox的属性

#include "..\..\Common\IO\AppDirectoryUtil.h"#include "..\..\Common\Database\DbConnection.h"#include "..\..\Common\Database\DbCommand.h"#include "..\..\Common\Database\DbDataReader.h" static CString GetConnectionStr

Android应用之——不要将数据存储在Application类中

前言:最近在开发中发现了一个比较严重的问题,当我们将应用按home键放入后台运行,一段时间后,当我们再次打开应用的时候,十有八九会出现一个NullPointException的空指针异常,根据logcat的日志,就会定位到一个去全局性到变量去,这是什么原因呢?原来,是因为我们我们将很多数据放入了application中作为全局变量,导致了问题的产生,下面来说下为什么不能将数据放在application中. 一.application类的简介 Application和Activity,Servic

类中函数

[1]空类为什么可以创建对象呢? 示例代码如下: 1 class Test 2 { 3 }; 4 void main() 5 { 6 Test t1; 7 cout<<sizeof(Test)<<endl; //1 8 } 让我们先看看这个例子.既然都没有构造函数,怎么实现对象t1的构建呢? 哦,经过大脑的回旋式搜索,忆得有一本书上说过,当用户定义一个空类(如上)时,编译器就会为这个类默认生成六个方法. 既然这是编译器完成的工作,那我们只要知道是那些方法就好了,其余就学习编译器的结