标签归档:properties

python中的属性文件(类似于Java属性)

问题:python中的属性文件(类似于Java属性)

给定以下格式(.properties.ini):

propertyName1=propertyValue1
propertyName2=propertyValue2
...
propertyNameN=propertyValueN

对于Java,有一个Properties类,该类提供了解析/与上述格式交互的功能。

python标准库(2.x)中有类似的东西吗?

如果没有,我还有什么其他选择?

Given the following format (.properties or .ini):

propertyName1=propertyValue1
propertyName2=propertyValue2
...
propertyNameN=propertyValueN

For Java there is the Properties class that offers functionality to parse / interact with the above format.

Is there something similar in python‘s standard library (2.x) ?

If not, what other alternatives do I have ?


回答 0

对于.ini文件,有一个ConfigParser模块,它提供与.ini文件兼容的格式。

无论如何,没有任何可用于解析完整的.properties文件的文件,当我必须这样做时,我只是使用jython(我在谈论脚本)。

For .ini files there is the ConfigParser module that provides a format compatible with .ini files.

Anyway there’s nothing available for parsing complete .properties files, when I have to do that I simply use jython (I’m talking about scripting).


回答 1

我能够使用它ConfigParser,没有人显示如何执行此操作的任何示例,因此这里是属性文件的简单python阅读器和属性文件的示例。请注意,扩展名仍然.properties,但是我必须添加一个节标题,类似于您在.ini文件中看到的标题……有点混蛋,但是可以工作。

python文件: PythonPropertyReader.py

#!/usr/bin/python    
import ConfigParser
config = ConfigParser.RawConfigParser()
config.read('ConfigFile.properties')

print config.get('DatabaseSection', 'database.dbname');

属性文件: ConfigFile.properties

[DatabaseSection]
database.dbname=unitTest
database.user=root
database.password=

有关更多功能,请阅读:https : //docs.python.org/2/library/configparser.html

I was able to get this to work with ConfigParser, no one showed any examples on how to do this, so here is a simple python reader of a property file and example of the property file. Note that the extension is still .properties, but I had to add a section header similar to what you see in .ini files… a bit of a bastardization, but it works.

The python file: PythonPropertyReader.py

#!/usr/bin/python    
import ConfigParser
config = ConfigParser.RawConfigParser()
config.read('ConfigFile.properties')

print config.get('DatabaseSection', 'database.dbname');

The property file: ConfigFile.properties

[DatabaseSection]
database.dbname=unitTest
database.user=root
database.password=

For more functionality, read: https://docs.python.org/2/library/configparser.html


回答 2

Java属性文件通常也是有效的python代码。您可以将myconfig.properties文件重命名为myconfig.py。然后像这样导入文件

import myconfig

并直接访问属性

print myconfig.propertyName1

A java properties file is often valid python code as well. You could rename your myconfig.properties file to myconfig.py. Then just import your file, like this

import myconfig

and access the properties directly

print myconfig.propertyName1

回答 3

我知道这是一个非常老的问题,但是我现在需要它,因此我决定实现自己的解决方案,一个纯python解决方案,它涵盖了大多数用例(不是全部):

def load_properties(filepath, sep='=', comment_char='#'):
    """
    Read the file passed as parameter as a properties file.
    """
    props = {}
    with open(filepath, "rt") as f:
        for line in f:
            l = line.strip()
            if l and not l.startswith(comment_char):
                key_value = l.split(sep)
                key = key_value[0].strip()
                value = sep.join(key_value[1:]).strip().strip('"') 
                props[key] = value 
    return props

您可以sep将’:’ 更改为以下格式的文件:

key : value

该代码可以正确解析以下行:

url = "http://my-host.com"
name = Paul = Pablo
# This comment line will be ignored

您将获得以下命令:

{"url": "http://my-host.com", "name": "Paul = Pablo" }

I know that this is a very old question, but I need it just now and I decided to implement my own solution, a pure python solution, that covers most uses cases (not all):

def load_properties(filepath, sep='=', comment_char='#'):
    """
    Read the file passed as parameter as a properties file.
    """
    props = {}
    with open(filepath, "rt") as f:
        for line in f:
            l = line.strip()
            if l and not l.startswith(comment_char):
                key_value = l.split(sep)
                key = key_value[0].strip()
                value = sep.join(key_value[1:]).strip().strip('"') 
                props[key] = value 
    return props

You can change the sep to ‘:’ to parse files with format:

key : value

The code parses correctly lines like:

url = "http://my-host.com"
name = Paul = Pablo
# This comment line will be ignored

You’ll get a dict with:

{"url": "http://my-host.com", "name": "Paul = Pablo" }

回答 4

如果可以选择文件格式,我建议使用.ini和Python的ConfigParser,如上所述。如果您需要与Java .properties文件兼容,那么我已经为它编写了一个名为jprops的库。我们使用的是pyjavaproperties,但是遇到各种限制后,我最终实现了自己的。它完全支持.properties格式,包括unicode支持和对转义序列的更好支持。Jprops还可以解析任何类似文件的对象,而pyjavaproperties仅适用于磁盘上的实际文件。

If you have an option of file formats I suggest using .ini and Python’s ConfigParser as mentioned. If you need compatibility with Java .properties files I have written a library for it called jprops. We were using pyjavaproperties, but after encountering various limitations I ended up implementing my own. It has full support for the .properties format, including unicode support and better support for escape sequences. Jprops can also parse any file-like object while pyjavaproperties only works with real files on disk.


回答 5

如果您没有多行属性并且需求非常简单,那么可以使用几行代码为您解决:

档案t.properties

a=b
c=d
e=f

Python代码:

with open("t.properties") as f:
    l = [line.split("=") for line in f.readlines()]
    d = {key.strip(): value.strip() for key, value in l}

if you don’t have multi line properties and a very simple need, a few lines of code can solve it for you:

File t.properties:

a=b
c=d
e=f

Python code:

with open("t.properties") as f:
    l = [line.split("=") for line in f.readlines()]
    d = {key.strip(): value.strip() for key, value in l}

回答 6

这不完全是属性,但是Python确实有一个很好的库来解析配置文件。另请参见此食谱:java.util.Properties的python替代品

This is not exactly properties but Python does have a nice library for parsing configuration files. Also see this recipe: A python replacement for java.util.Properties.


回答 7

这是我的项目的链接:https : //sourceforge.net/projects/pyproperties/。它是一个库,其中包含用于处理Python 3.x的* .properties文件的方法。

但这不是基于java.util.Properties

Here is link to my project: https://sourceforge.net/projects/pyproperties/. It is a library with methods for working with *.properties files for Python 3.x.

But it is not based on java.util.Properties


回答 8

这个是java.util.Propeties的一对一替换

从文档中:

  def __parse(self, lines):
        """ Parse a list of lines and create
        an internal property dictionary """

        # Every line in the file must consist of either a comment
        # or a key-value pair. A key-value pair is a line consisting
        # of a key which is a combination of non-white space characters
        # The separator character between key-value pairs is a '=',
        # ':' or a whitespace character not including the newline.
        # If the '=' or ':' characters are found, in the line, even
        # keys containing whitespace chars are allowed.

        # A line with only a key according to the rules above is also
        # fine. In such case, the value is considered as the empty string.
        # In order to include characters '=' or ':' in a key or value,
        # they have to be properly escaped using the backslash character.

        # Some examples of valid key-value pairs:
        #
        # key     value
        # key=value
        # key:value
        # key     value1,value2,value3
        # key     value1,value2,value3 \
        #         value4, value5
        # key
        # This key= this value
        # key = value1 value2 value3

        # Any line that starts with a '#' is considerered a comment
        # and skipped. Also any trailing or preceding whitespaces
        # are removed from the key/value.

        # This is a line parser. It parses the
        # contents like by line.

This is a one-to-one replacement of java.util.Propeties

From the doc:

  def __parse(self, lines):
        """ Parse a list of lines and create
        an internal property dictionary """

        # Every line in the file must consist of either a comment
        # or a key-value pair. A key-value pair is a line consisting
        # of a key which is a combination of non-white space characters
        # The separator character between key-value pairs is a '=',
        # ':' or a whitespace character not including the newline.
        # If the '=' or ':' characters are found, in the line, even
        # keys containing whitespace chars are allowed.

        # A line with only a key according to the rules above is also
        # fine. In such case, the value is considered as the empty string.
        # In order to include characters '=' or ':' in a key or value,
        # they have to be properly escaped using the backslash character.

        # Some examples of valid key-value pairs:
        #
        # key     value
        # key=value
        # key:value
        # key     value1,value2,value3
        # key     value1,value2,value3 \
        #         value4, value5
        # key
        # This key= this value
        # key = value1 value2 value3

        # Any line that starts with a '#' is considerered a comment
        # and skipped. Also any trailing or preceding whitespaces
        # are removed from the key/value.

        # This is a line parser. It parses the
        # contents like by line.

回答 9

您可以在ConfigParser.RawConfigParser.readfp此处定义的类似文件的对象中使用-> https://docs.python.org/2/library/configparser.html#ConfigParser.RawConfigParser.readfp

定义一个覆盖的类 readline在属性文件的实际内容之前添加节名称。

我已将其打包到返回dict定义的所有属性的类中。

import ConfigParser

class PropertiesReader(object):

    def __init__(self, properties_file_name):
        self.name = properties_file_name
        self.main_section = 'main'

        # Add dummy section on top
        self.lines = [ '[%s]\n' % self.main_section ]

        with open(properties_file_name) as f:
            self.lines.extend(f.readlines())

        # This makes sure that iterator in readfp stops
        self.lines.append('')

    def readline(self):
        return self.lines.pop(0)

    def read_properties(self):
        config = ConfigParser.RawConfigParser()

        # Without next line the property names will be lowercased
        config.optionxform = str

        config.readfp(self)
        return dict(config.items(self.main_section))

if __name__ == '__main__':
    print PropertiesReader('/path/to/file.properties').read_properties()

You can use a file-like object in ConfigParser.RawConfigParser.readfp defined here -> https://docs.python.org/2/library/configparser.html#ConfigParser.RawConfigParser.readfp

Define a class that overrides readline that adds a section name before the actual contents of your properties file.

I’ve packaged it into the class that returns a dict of all the properties defined.

import ConfigParser

class PropertiesReader(object):

    def __init__(self, properties_file_name):
        self.name = properties_file_name
        self.main_section = 'main'

        # Add dummy section on top
        self.lines = [ '[%s]\n' % self.main_section ]

        with open(properties_file_name) as f:
            self.lines.extend(f.readlines())

        # This makes sure that iterator in readfp stops
        self.lines.append('')

    def readline(self):
        return self.lines.pop(0)

    def read_properties(self):
        config = ConfigParser.RawConfigParser()

        # Without next line the property names will be lowercased
        config.optionxform = str

        config.readfp(self)
        return dict(config.items(self.main_section))

if __name__ == '__main__':
    print PropertiesReader('/path/to/file.properties').read_properties()

回答 10

我已经用过了,这个库非常有用

from pyjavaproperties import Properties
p = Properties()
p.load(open('test.properties'))
p.list()
print(p)
print(p.items())
print(p['name3'])
p['name3'] = 'changed = value'

i have used this, this library is very useful

from pyjavaproperties import Properties
p = Properties()
p.load(open('test.properties'))
p.list()
print(p)
print(p.items())
print(p['name3'])
p['name3'] = 'changed = value'

回答 11

这就是我在项目中所做的事情:我只创建了另一个名为properties.py的.py文件,其中包含我在项目中使用的所有常见变量/属性,并且在任何文件中都需要引用这些变量,

from properties import *(or anything you need)

当我经常更改开发人员的位置并且某些常见变量与本地环境有关时,使用此方法来保持svn的和平。对我来说效果很好,但不确定是否可以在正式的开发环境中建议使用此方法。

This is what I’m doing in my project: I just create another .py file called properties.py which includes all common variables/properties I used in the project, and in any file need to refer to these variables, put

from properties import *(or anything you need)

Used this method to keep svn peace when I was changing dev locations frequently and some common variables were quite relative to local environment. Works fine for me but not sure this method would be suggested for formal dev environment etc.


回答 12

import json
f=open('test.json')
x=json.load(f)
f.close()
print(x)

test.json的内容:{“主机”:“ 127.0.0.1”,“用户”:“ jms”}

import json
f=open('test.json')
x=json.load(f)
f.close()
print(x)

Contents of test.json: {“host”: “127.0.0.1”, “user”: “jms”}


回答 13

我创建了一个与Java的Properties类几乎相似的python模块(实际上就像Spring中的PropertyPlaceholderConfigurer一样,它允许您使用$ {variable-reference}来引用已定义的property)

编辑:您可以通过运行命令(当前已针对python 3测试)来安装此软件包。
pip install property

该项目托管在GitHub上

示例:(详细文档可在此处找到)

假设您在my_file.properties文件中定义了以下属性

foo = I am awesome
bar = ${chocolate}-bar
chocolate = fudge

加载上述属性的代码

from properties.p import Property

prop = Property()
# Simply load it into a dictionary
dic_prop = prop.load_property_files('my_file.properties')

I have created a python module that is almost similar to the Properties class of Java ( Actually it is like the PropertyPlaceholderConfigurer in spring which lets you use ${variable-reference} to refer to already defined property )

EDIT : You may install this package by running the command(currently tested for python 3).
pip install property

The project is hosted on GitHub

Example : ( Detailed documentation can be found here )

Let’s say you have the following properties defined in my_file.properties file

foo = I am awesome
bar = ${chocolate}-bar
chocolate = fudge

Code to load the above properties

from properties.p import Property

prop = Property()
# Simply load it into a dictionary
dic_prop = prop.load_property_files('my_file.properties')

回答 14

如果您需要以简单的方式从属性文件中的部分读取所有值:

您的config.properties文件布局:

[SECTION_NAME]  
key1 = value1  
key2 = value2  

您的代码:

   import configparser

   config = configparser.RawConfigParser()
   config.read('path_to_config.properties file')

   details_dict = dict(config.items('SECTION_NAME'))

这将为您提供一个字典,其中的键与配置文件中的键及其相应值相同。

details_dict 是:

{'key1':'value1', 'key2':'value2'}

现在获取key1的值: details_dict['key1']

将所有内容放到仅从配置文件读取该部分一次的方法中(在程序运行期间第一次调用该方法)。

def get_config_dict():
    if not hasattr(get_config_dict, 'config_dict'):
        get_config_dict.config_dict = dict(config.items('SECTION_NAME'))
    return get_config_dict.config_dict

现在调用上面的函数并获取所需键的值:

config_details = get_config_dict()
key_1_value = config_details['key1'] 

————————————————– ———–

扩展上述方法,自动逐节阅读,然后按节名和键名进行访问。

def get_config_section():
    if not hasattr(get_config_section, 'section_dict'):
        get_config_section.section_dict = dict()

        for section in config.sections():
            get_config_section.section_dict[section] = 
                             dict(config.items(section))

    return get_config_section.section_dict

要访问:

config_dict = get_config_section()

port = config_dict['DB']['port'] 

(此处“ DB”是配置文件中的节名称,“端口”是“ DB”节下的键。)

If you need to read all values from a section in properties file in a simple manner:

Your config.properties file layout :

[SECTION_NAME]  
key1 = value1  
key2 = value2  

You code:

   import configparser

   config = configparser.RawConfigParser()
   config.read('path_to_config.properties file')

   details_dict = dict(config.items('SECTION_NAME'))

This will give you a dictionary where keys are same as in config file and their corresponding values.

details_dict is :

{'key1':'value1', 'key2':'value2'}

Now to get key1’s value : details_dict['key1']

Putting it all in a method which reads that section from config file only once(the first time the method is called during a program run).

def get_config_dict():
    if not hasattr(get_config_dict, 'config_dict'):
        get_config_dict.config_dict = dict(config.items('SECTION_NAME'))
    return get_config_dict.config_dict

Now call the above function and get the required key’s value :

config_details = get_config_dict()
key_1_value = config_details['key1'] 

————————————————————-

Extending the approach mentioned above, reading section by section automatically and then accessing by section name followed by key name.

def get_config_section():
    if not hasattr(get_config_section, 'section_dict'):
        get_config_section.section_dict = dict()

        for section in config.sections():
            get_config_section.section_dict[section] = 
                             dict(config.items(section))

    return get_config_section.section_dict

To access:

config_dict = get_config_section()

port = config_dict['DB']['port'] 

(here ‘DB’ is a section name in config file and ‘port’ is a key under section ‘DB’.)


回答 15

下面的两行代码显示了如何使用Python List Comprehension加载“ java样式”属性文件。

split_properties=[line.split("=") for line in open('/<path_to_property_file>)]
properties={key: value for key,value in split_properties }

请查看以下帖子以了解详细信息 https://ilearnonlinesite.wordpress.com/2017/07/24/reading-property-file-in-python-using-comprehension-and-generators/

Below 2 lines of code shows how to use Python List Comprehension to load ‘java style’ property file.

split_properties=[line.split("=") for line in open('/<path_to_property_file>)]
properties={key: value for key,value in split_properties }

Please have a look at below post for details https://ilearnonlinesite.wordpress.com/2017/07/24/reading-property-file-in-python-using-comprehension-and-generators/


回答 16

您可以将参数“ fromfile_prefix_chars”与argparse一起使用,以从配置文件中读取内容,如下所示-

临时

parser = argparse.ArgumentParser(fromfile_prefix_chars='#')
parser.add_argument('--a')
parser.add_argument('--b')
args = parser.parse_args()
print(args.a)
print(args.b)

配置文件

--a
hello
--b
hello dear

运行命令

python temp.py "#config"

you can use parameter “fromfile_prefix_chars” with argparse to read from config file as below—

temp.py

parser = argparse.ArgumentParser(fromfile_prefix_chars='#')
parser.add_argument('--a')
parser.add_argument('--b')
args = parser.parse_args()
print(args.a)
print(args.b)

config file

--a
hello
--b
hello dear

Run command

python temp.py "#config"

回答 17

我使用ConfigParser进行了如下操作。该代码假定在放置BaseTest的同一目录中有一个名为config.prop的文件:

配置文件

[CredentialSection]
app.name=MyAppName

BaseTest.py:

import unittest
import ConfigParser

class BaseTest(unittest.TestCase):
    def setUp(self):
        __SECTION = 'CredentialSection'
        config = ConfigParser.ConfigParser()
        config.readfp(open('config.prop'))
        self.__app_name = config.get(__SECTION, 'app.name')

    def test1(self):
        print self.__app_name % This should print: MyAppName

I did this using ConfigParser as follows. The code assumes that there is a file called config.prop in the same directory where BaseTest is placed:

config.prop

[CredentialSection]
app.name=MyAppName

BaseTest.py:

import unittest
import ConfigParser

class BaseTest(unittest.TestCase):
    def setUp(self):
        __SECTION = 'CredentialSection'
        config = ConfigParser.ConfigParser()
        config.readfp(open('config.prop'))
        self.__app_name = config.get(__SECTION, 'app.name')

    def test1(self):
        print self.__app_name % This should print: MyAppName

回答 18

这就是我编写的用于解析文件并将其设置为env变量的内容,该变量会跳过注释,并且非关键值行添加了开关来指定hg:d

  • -h或–help打印用法摘要
  • -c指定用于标识注释的字符
  • -s prop文件中键和值之间的分隔符
  • 并指定需要解析的属性文件,例如:python EnvParamSet.py -c#-s = env.properties

    import pipes
    import sys , getopt
    import os.path
    
    class Parsing :
    
            def __init__(self , seprator , commentChar , propFile):
            self.seprator = seprator
            self.commentChar = commentChar
            self.propFile  = propFile
    
        def  parseProp(self):
            prop = open(self.propFile,'rU')
            for line in prop :
                if line.startswith(self.commentChar)==False and  line.find(self.seprator) != -1  :
                    keyValue = line.split(self.seprator)
                    key =  keyValue[0].strip() 
                    value = keyValue[1].strip() 
                            print("export  %s=%s" % (str (key),pipes.quote(str(value))))
    
    
    
    
    class EnvParamSet:
    
        def main (argv):
    
            seprator = '='
            comment =  '#'
    
            if len(argv)  is 0:
                print "Please Specify properties file to be parsed "
                sys.exit()
            propFile=argv[-1] 
    
    
            try :
                opts, args = getopt.getopt(argv, "hs:c:f:", ["help", "seprator=","comment=", "file="])
            except getopt.GetoptError,e:
                print str(e)
                print " possible  arguments  -s <key value sperator > -c < comment char >    <file> \n  Try -h or --help "
                sys.exit(2)
    
    
            if os.path.isfile(args[0])==False:
                print "File doesnt exist "
                sys.exit()
    
    
            for opt , arg  in opts :
                if opt in ("-h" , "--help"):
                    print " hg:d  \n -h or --help print usage summary \n -c Specify char that idetifes comment  \n -s Sperator between key and value in prop file \n  specify file  "
                    sys.exit()
                elif opt in ("-s" , "--seprator"):
                    seprator = arg 
                elif opt in ("-c"  , "--comment"):
                    comment  = arg
    
            p = Parsing( seprator, comment , propFile)
            p.parseProp()
    
        if __name__ == "__main__":
                main(sys.argv[1:])

This is what i had written to parse file and set it as env variables which skips comments and non key value lines added switches to specify hg:d

  • -h or –help print usage summary
  • -c Specify char that identifies comment
  • -s Separator between key and value in prop file
  • and specify properties file that needs to be parsed eg : python EnvParamSet.py -c # -s = env.properties

    import pipes
    import sys , getopt
    import os.path
    
    class Parsing :
    
            def __init__(self , seprator , commentChar , propFile):
            self.seprator = seprator
            self.commentChar = commentChar
            self.propFile  = propFile
    
        def  parseProp(self):
            prop = open(self.propFile,'rU')
            for line in prop :
                if line.startswith(self.commentChar)==False and  line.find(self.seprator) != -1  :
                    keyValue = line.split(self.seprator)
                    key =  keyValue[0].strip() 
                    value = keyValue[1].strip() 
                            print("export  %s=%s" % (str (key),pipes.quote(str(value))))
    
    
    
    
    class EnvParamSet:
    
        def main (argv):
    
            seprator = '='
            comment =  '#'
    
            if len(argv)  is 0:
                print "Please Specify properties file to be parsed "
                sys.exit()
            propFile=argv[-1] 
    
    
            try :
                opts, args = getopt.getopt(argv, "hs:c:f:", ["help", "seprator=","comment=", "file="])
            except getopt.GetoptError,e:
                print str(e)
                print " possible  arguments  -s <key value sperator > -c < comment char >    <file> \n  Try -h or --help "
                sys.exit(2)
    
    
            if os.path.isfile(args[0])==False:
                print "File doesnt exist "
                sys.exit()
    
    
            for opt , arg  in opts :
                if opt in ("-h" , "--help"):
                    print " hg:d  \n -h or --help print usage summary \n -c Specify char that idetifes comment  \n -s Sperator between key and value in prop file \n  specify file  "
                    sys.exit()
                elif opt in ("-s" , "--seprator"):
                    seprator = arg 
                elif opt in ("-c"  , "--comment"):
                    comment  = arg
    
            p = Parsing( seprator, comment , propFile)
            p.parseProp()
    
        if __name__ == "__main__":
                main(sys.argv[1:])
    

回答 19

Lightbend发布了Typesafe Config库,该库可解析属性文件以及一些基于JSON的扩展。Lightbend的库仅用于JVM,但似乎已被广泛采用,并且现在有许多语言的端口,包括Python:https : //github.com/chimpler/pyhocon

Lightbend has released the Typesafe Config library, which parses properties files and also some JSON-based extensions. Lightbend’s library is only for the JVM, but it seems to be widely adopted and there are now ports in many languages, including Python: https://github.com/chimpler/pyhocon


回答 20

您可以使用以下函数,它是@mvallebr的修改代码。它尊重属性文件的注释,忽略空的新行,并允许检索单个键值。

def getProperties(propertiesFile ="/home/memin/.config/customMemin/conf.properties", key=''):
    """
    Reads a .properties file and returns the key value pairs as dictionary.
    if key value is specified, then it will return its value alone.
    """
    with open(propertiesFile) as f:
        l = [line.strip().split("=") for line in f.readlines() if not line.startswith('#') and line.strip()]
        d = {key.strip(): value.strip() for key, value in l}

        if key:
            return d[key]
        else:
            return d

You can use the following function, which is the modified code of @mvallebr. It respects the properties file comments, ignores empty new lines, and allows retrieving a single key value.

def getProperties(propertiesFile ="/home/memin/.config/customMemin/conf.properties", key=''):
    """
    Reads a .properties file and returns the key value pairs as dictionary.
    if key value is specified, then it will return its value alone.
    """
    with open(propertiesFile) as f:
        l = [line.strip().split("=") for line in f.readlines() if not line.startswith('#') and line.strip()]
        d = {key.strip(): value.strip() for key, value in l}

        if key:
            return d[key]
        else:
            return d

回答 21

这对我有用。

from pyjavaproperties import Properties
p = Properties()
p.load(open('test.properties'))
p.list()
print p
print p.items()
print p['name3']

this works for me.

from pyjavaproperties import Properties
p = Properties()
p.load(open('test.properties'))
p.list()
print p
print p.items()
print p['name3']

回答 22

我遵循configparser方法,对我来说效果很好。创建了一个PropertyReader文件,并在其中使用了配置解析器以准备与每个部分相对应的属性。

**使用Python 2.7

PropertyReader.py文件的内容:

#!/usr/bin/python
import ConfigParser

class PropertyReader:

def readProperty(self, strSection, strKey):
    config = ConfigParser.RawConfigParser()
    config.read('ConfigFile.properties')
    strValue = config.get(strSection,strKey);
    print "Value captured for "+strKey+" :"+strValue
    return strValue

读取架构文件的内容:

from PropertyReader import *

class ReadSchema:

print PropertyReader().readProperty('source1_section','source_name1')
print PropertyReader().readProperty('source2_section','sn2_sc1_tb')

.properties文件的内容:

[source1_section]
source_name1:module1
sn1_schema:schema1,schema2,schema3
sn1_sc1_tb:employee,department,location
sn1_sc2_tb:student,college,country

[source2_section]
source_name1:module2
sn2_schema:schema4,schema5,schema6
sn2_sc1_tb:employee,department,location
sn2_sc2_tb:student,college,country

I followed configparser approach and it worked quite well for me. Created one PropertyReader file and used config parser there to ready property to corresponding to each section.

**Used Python 2.7

Content of PropertyReader.py file:

#!/usr/bin/python
import ConfigParser

class PropertyReader:

def readProperty(self, strSection, strKey):
    config = ConfigParser.RawConfigParser()
    config.read('ConfigFile.properties')
    strValue = config.get(strSection,strKey);
    print "Value captured for "+strKey+" :"+strValue
    return strValue

Content of read schema file:

from PropertyReader import *

class ReadSchema:

print PropertyReader().readProperty('source1_section','source_name1')
print PropertyReader().readProperty('source2_section','sn2_sc1_tb')

Content of .properties file:

[source1_section]
source_name1:module1
sn1_schema:schema1,schema2,schema3
sn1_sc1_tb:employee,department,location
sn1_sc2_tb:student,college,country

[source2_section]
source_name1:module2
sn2_schema:schema4,schema5,schema6
sn2_sc1_tb:employee,department,location
sn2_sc2_tb:student,college,country

回答 23

在python模块中创建一个字典并将所有内容存储到其中并访问它,例如:

dict = {
       'portalPath' : 'www.xyx.com',
       'elementID': 'submit'}

现在访问它,您只需执行以下操作:

submitButton = driver.find_element_by_id(dict['elementID'])

create a dictionary in your python module and store everything into it and access it, for example:

dict = {
       'portalPath' : 'www.xyx.com',
       'elementID': 'submit'}

Now to access it you can simply do:

submitButton = driver.find_element_by_id(dict['elementID'])

如何在Python中枚举对象的属性?

问题:如何在Python中枚举对象的属性?

IC#我们通过反思来做到。在Javascript中,它很简单:

for(var propertyName in objectName)
    var currentPropertyValue = objectName[propertyName];

如何在Python中完成?

I C# we do it through reflection. In Javascript it is simple as:

for(var propertyName in objectName)
    var currentPropertyValue = objectName[propertyName];

How to do it in Python?


回答 0

for property, value in vars(theObject).iteritems():
    print property, ": ", value

请注意,在极少数情况下,有一个__slots__属性,此类通常没有属性__dict__

for property, value in vars(theObject).items():
    print(property, ":", value)

Be aware that in some rare cases there’s a __slots__ property, such classes often have no __dict__.


回答 1

请参阅inspect.getmembers(object[, predicate])

返回按名称排序的(名称,值)对列表中的对象的所有成员。如果提供了可选的谓词参数,则仅包含谓词为其返回真值的成员。

>>> [name for name,thing in inspect.getmembers([])]
['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', 
'__delslice__',    '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', 
'__getitem__', '__getslice__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__iter__', 
'__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__','__reduce_ex__', 
'__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__setslice__', 
'__sizeof__', '__str__', '__subclasshook__', 'append', 'count', 'extend', 'index', 
'insert', 'pop', 'remove', 'reverse', 'sort']
>>> 

See inspect.getmembers(object[, predicate]).

Return all the members of an object in a list of (name, value) pairs sorted by name. If the optional predicate argument is supplied, only members for which the predicate returns a true value are included.

>>> [name for name,thing in inspect.getmembers([])]
['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', 
'__delslice__',    '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', 
'__getitem__', '__getslice__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__iter__', 
'__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__','__reduce_ex__', 
'__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__setslice__', 
'__sizeof__', '__str__', '__subclasshook__', 'append', 'count', 'extend', 'index', 
'insert', 'pop', 'remove', 'reverse', 'sort']
>>> 

回答 2

dir()是简单的方法。看这里:

Python自省指南


回答 3

__dict__对象的属性是其所有其他定义的属性的字典。请注意,Python类可以覆盖getattr 并使内容看起来像属性,而不是in __dict__。还有一些内置函数vars()dir()它们在微妙的方式上有所不同。并且__slots__可以代替__dict__一些不寻常的类。

Python中的对象很复杂。__dict__是开始进行反射式编程的正确位置。dir()如果您是在交互式shell中四处乱逛,那么这是一个开始的地方。

The __dict__ property of the object is a dictionary of all its other defined properties. Note that Python classes can override getattr and make things that look like properties but are not in__dict__. There’s also the builtin functions vars() and dir() which are different in subtle ways. And __slots__ can replace __dict__ in some unusual classes.

Objects are complicated in Python. __dict__ is the right place to start for reflection-style programming. dir() is the place to start if you’re hacking around in an interactive shell.


回答 4

georg scholly短版

print vars(theObject)

for one-liners:

print vars(theObject)

回答 5

如果您正在寻找所有属性的反映,那么上面的答案很好。

如果您只是想获取字典的键(与Python中的“对象”不同),请使用

my_dict.keys()

my_dict = {'abc': {}, 'def': 12, 'ghi': 'string' }
my_dict.keys() 
> ['abc', 'def', 'ghi']

If you’re looking for reflection of all properties, the answers above are great.

If you’re simply looking to get the keys of a dictionary (which is different from an ‘object’ in Python), use

my_dict.keys()

my_dict = {'abc': {}, 'def': 12, 'ghi': 'string' }
my_dict.keys() 
> ['abc', 'def', 'ghi']

回答 6

其他答案完全涵盖了这一点,但我将使其明确。对象可以具有类属性以及静态和动态实例属性。

class foo:
    classy = 1
    @property
    def dyno(self):
        return 1
    def __init__(self):
        self.stasis = 2

    def fx(self):
        return 3

stasis是静态的,dyno是动态的(请参阅属性装饰器),并且classy是类属性。如果我们简单地做,__dict__否则vars我们只会得到静态的。

o = foo()
print(o.__dict__) #{'stasis': 2}
print(vars(o)) #{'stasis': 2}

因此,如果我们希望其他人__dict__都能得到一切(甚至更多)。这包括魔术方法和属性以及法线绑定方法。因此,请避免这些情况:

d = {k: getattr(o, k, '') for k in o.__dir__() if k[:2] != '__' and type(getattr(o, k, '')).__name__ != 'method'}
print(d) #{'stasis': 2, 'classy': 1, 'dyno': 1}

type具有属性修饰方法(动态属性)的调用将为您提供返回值的类型,而不是method。为了证明这一点,让我们用json将其字符串化:

import json
print(json.dumps(d)) #{"stasis": 2, "classy": 1, "dyno": 1}

如果这是一种方法,它将崩溃。

TL; DR。尝试同时调用extravar = lambda o: {k: getattr(o, k, '') for k in o.__dir__() if k[:2] != '__' and type(getattr(o, k, '')).__name__ != 'method'}这三个方法,但不要调用方法或魔术。

This is totally covered by the other answers, but I’ll make it explicit. An object may have class attributes and static and dynamic instance attributes.

class foo:
    classy = 1
    @property
    def dyno(self):
        return 1
    def __init__(self):
        self.stasis = 2

    def fx(self):
        return 3

stasis is static, dyno is dynamic (cf. property decorator) and classy is a class attribute. If we simply do __dict__ or vars we will only get the static one.

o = foo()
print(o.__dict__) #{'stasis': 2}
print(vars(o)) #{'stasis': 2}

So if we want the others __dict__ will get everything (and more). This includes magic methods and attributes and normal bound methods. So lets avoid those:

d = {k: getattr(o, k, '') for k in o.__dir__() if k[:2] != '__' and type(getattr(o, k, '')).__name__ != 'method'}
print(d) #{'stasis': 2, 'classy': 1, 'dyno': 1}

The type called with a property decorated method (a dynamic attribute) will give you the type of the returned value, not method. To prove this let’s json stringify it:

import json
print(json.dumps(d)) #{"stasis": 2, "classy": 1, "dyno": 1}

Had it been a method it would have crashed.

TL;DR. try calling extravar = lambda o: {k: getattr(o, k, '') for k in o.__dir__() if k[:2] != '__' and type(getattr(o, k, '')).__name__ != 'method'} for all three, but not methods nor magic.


如何在python抽象类中创建抽象属性

问题:如何在python抽象类中创建抽象属性

在以下代码中,我创建了一个基本抽象类Base。我希望所有从其继承的类都Base提供该name属性,因此我将该属性设置为@abstractmethod

然后,我创建了一个Base名为的子类,该子类Base_1旨在提供一些功能,但仍保持抽象。中没有name属性Base_1,但是python实例化了该类的对象而没有错误。一个人如何创建抽象属性?

from abc import ABCMeta, abstractmethod
class Base(object):
    __metaclass__ = ABCMeta
    def __init__(self, strDirConfig):
        self.strDirConfig = strDirConfig

    @abstractmethod
    def _doStuff(self, signals):
        pass

    @property    
    @abstractmethod
    def name(self):
        #this property will be supplied by the inheriting classes
        #individually
        pass


class Base_1(Base):
    __metaclass__ = ABCMeta
    # this class does not provide the name property, should raise an error
    def __init__(self, strDirConfig):
        super(Base_1, self).__init__(strDirConfig)

    def _doStuff(self, signals):
        print 'Base_1 does stuff'


class C(Base_1):
    @property
    def name(self):
        return 'class C'


if __name__ == '__main__':
    b1 = Base_1('abc')  

In the following code, I create a base abstract class Base. I want all the classes that inherit from Base to provide the name property, so I made this property an @abstractmethod.

Then I created a subclass of Base, called Base_1, which is meant to supply some functionality, but still remain abstract. There is no name property in Base_1, but nevertheless python instatinates an object of that class without an error. How does one create abstract properties?

from abc import ABCMeta, abstractmethod
class Base(object):
    __metaclass__ = ABCMeta
    def __init__(self, strDirConfig):
        self.strDirConfig = strDirConfig

    @abstractmethod
    def _doStuff(self, signals):
        pass

    @property    
    @abstractmethod
    def name(self):
        #this property will be supplied by the inheriting classes
        #individually
        pass


class Base_1(Base):
    __metaclass__ = ABCMeta
    # this class does not provide the name property, should raise an error
    def __init__(self, strDirConfig):
        super(Base_1, self).__init__(strDirConfig)

    def _doStuff(self, signals):
        print 'Base_1 does stuff'


class C(Base_1):
    @property
    def name(self):
        return 'class C'


if __name__ == '__main__':
    b1 = Base_1('abc')  

回答 0

Python 3.3开始,修复了一个错误,这意味着property()装饰器现在应用于抽象方法时,可以正确地标识为抽象。

注:订单的问题,你必须使用@property@abstractmethod

Python 3.3以上版本:python docs):

class C(ABC):
    @property
    @abstractmethod
    def my_abstract_property(self):
        ...

Python 2:python docs

class C(ABC):
    @abstractproperty
    def my_abstract_property(self):
        ...

Since Python 3.3 a bug was fixed meaning the property() decorator is now correctly identified as abstract when applied to an abstract method.

Note: Order matters, you have to use @property before @abstractmethod

Python 3.3+: (python docs):

class C(ABC):
    @property
    @abstractmethod
    def my_abstract_property(self):
        ...

Python 2: (python docs)

class C(ABC):
    @abstractproperty
    def my_abstract_property(self):
        ...

回答 1

Python 3.3之前,您不能嵌套@abstractmethod@property

使用@abstractproperty创建抽象属性(文档)。

from abc import ABCMeta, abstractmethod, abstractproperty

class Base(object):
    # ...
    @abstractproperty
    def name(self):
        pass

该代码现在引发正确的异常:

追溯(最近一次通话):
  在第36行的文件“ foo.py”中 
    b1 = Base_1('abc')  
TypeError:无法使用抽象方法名称实例化抽象类Base_1

Until Python 3.3, you cannot nest @abstractmethod and @property.

Use @abstractproperty to create abstract properties (docs).

from abc import ABCMeta, abstractmethod, abstractproperty

class Base(object):
    # ...
    @abstractproperty
    def name(self):
        pass

The code now raises the correct exception:

Traceback (most recent call last):
  File "foo.py", line 36, in 
    b1 = Base_1('abc')  
TypeError: Can't instantiate abstract class Base_1 with abstract methods name

回答 2

根据上面的詹姆斯回答

def compatibleabstractproperty(func):

    if sys.version_info > (3, 3):             
        return property(abstractmethod(func))
    else:
        return abstractproperty(func)

并将其用作装饰器

@compatibleabstractproperty
def env(self):
    raise NotImplementedError()

Based on James answer above

def compatibleabstractproperty(func):

    if sys.version_info > (3, 3):             
        return property(abstractmethod(func))
    else:
        return abstractproperty(func)

and use it as a decorator

@compatibleabstractproperty
def env(self):
    raise NotImplementedError()

Python只读属性

问题:Python只读属性

我不知道何时属性应该是私有的,是否应该使用属性。

我最近读到,setter和getters不是pythonic,我应该使用属性装饰器。没关系。

但是,如果我有属性,该属性不能从类外部设置,而是可以读取的(只读属性)。这个属性应该是私有的self._x吗?我所说的私有是指下划线吗?如果是,那么不使用getter怎么读?我现在知道的唯一方法是写

@property
def x(self):
    return self._x

这样我就可以读取属性,obj.x但是我无法设置它,obj.x = 1所以很好。

但是,我真的应该在乎设置不应该设置的对象吗?也许我应该离开它。但是话又说回来,我不能使用下划线,因为阅读obj._x对于用户来说很奇怪,所以我应该使用下划线obj.x,然后用户又一次不知道他一定不能设置该属性。

您的看法和做法是什么?

I don’t know when attribute should be private and if I should use property.

I read recently that setters and getters are not pythonic and I should use property decorator. It’s ok.

But what if I have attribute, that mustn’t be set from outside of class but can be read (read-only attribute). Should this attribute be private, and by private I mean with underscore, like that self._x? If yes then how can I read it without using getter? Only method I know right now is to write

@property
def x(self):
    return self._x

That way I can read attribute by obj.x but I can’t set it obj.x = 1 so it’s fine.

But should I really care about setting object that mustn’t be set? Maybe I should just leave it. But then again I can’t use underscore because reading obj._x is odd for user, so I should use obj.x and then again user doesn’t know that he mustn’t set this attribute.

What’s your opinion and practics?


回答 0

通常,在编写Python程序时应假定所有用户都同意成年人,因此他们有责任自己正确使用事物。但是,在极少数情况下,无法设置属性(例如派生值或从某个静态数据源读取的值)就没有意义,仅使用吸气剂的属性通常是首选模式。

Generally, Python programs should be written with the assumption that all users are consenting adults, and thus are responsible for using things correctly themselves. However, in the rare instance where it just does not make sense for an attribute to be settable (such as a derived value, or a value read from some static datasource), the getter-only property is generally the preferred pattern.


回答 1

西拉斯·雷Silas Ray)只是我的两分钱,走在正确的轨道上,但是我觉得自己想举个例子。;-)

Python是一种类型不安全的语言,因此,您始终必须信任代码的用户才能像合理的(明智的)人员一样使用代码。

根据PEP 8

仅对非公共方法和实例变量使用前导下划线。

要在类中具有“只读”属性,您可以使用@property修饰,您需要在继承object时使用新样式的类来进行继承。

例:

>>> class A(object):
...     def __init__(self, a):
...         self._a = a
...
...     @property
...     def a(self):
...         return self._a
... 
>>> a = A('test')
>>> a.a
'test'
>>> a.a = 'pleh'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: can't set attribute

Just my two cents, Silas Ray is on the right track, however I felt like adding an example. ;-)

Python is a type-unsafe language and thus you’ll always have to trust the users of your code to use the code like a reasonable (sensible) person.

Per PEP 8:

Use one leading underscore only for non-public methods and instance variables.

To have a ‘read-only’ property in a class you can make use of the @property decoration, you’ll need to inherit from object when you do so to make use of the new-style classes.

Example:

>>> class A(object):
...     def __init__(self, a):
...         self._a = a
...
...     @property
...     def a(self):
...         return self._a
... 
>>> a = A('test')
>>> a.a
'test'
>>> a.a = 'pleh'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: can't set attribute

回答 2

这是一种避免假设的方法

所有使用者都是成年人,因此有责任自行正确使用事物。

请在下面查看我的更新

使用@property,非常冗长,例如:

   class AClassWithManyAttributes:
        '''refactored to properties'''
        def __init__(a, b, c, d, e ...)
             self._a = a
             self._b = b
             self._c = c
             self.d = d
             self.e = e

        @property
        def a(self):
            return self._a
        @property
        def b(self):
            return self._b
        @property
        def c(self):
            return self._c
        # you get this ... it's long

使用

没有下划线:这是一个公共变量。
一个下划线:这是一个受保护的变量。
有两个下划线:这是一个私有变量。

除了最后一个,这是一个约定。如果您确实努力尝试,仍然可以使用双下划线访问变量。

那么我们该怎么办?我们是否放弃使用Python中的只读属性?

看哪!read_only_properties装潢抢救!

@read_only_properties('readonly', 'forbidden')
class MyClass(object):
    def __init__(self, a, b, c):
        self.readonly = a
        self.forbidden = b
        self.ok = c

m = MyClass(1, 2, 3)
m.ok = 4
# we can re-assign a value to m.ok
# read only access to m.readonly is OK 
print(m.ok, m.readonly) 
print("This worked...")
# this will explode, and raise AttributeError
m.forbidden = 4

你问:

哪里read_only_properties来的?

很高兴您询问,这是read_only_properties的来源:

def read_only_properties(*attrs):

    def class_rebuilder(cls):
        "The class decorator"

        class NewClass(cls):
            "This is the overwritten class"
            def __setattr__(self, name, value):
                if name not in attrs:
                    pass
                elif name not in self.__dict__:
                    pass
                else:
                    raise AttributeError("Can't modify {}".format(name))

                super().__setattr__(name, value)
        return NewClass
    return class_rebuilder

更新

我没想到这个答案会引起如此多的关注。令人惊讶的是。这鼓励我创建一个可以使用的软件包。

$ pip install read-only-properties

在您的python shell中:

In [1]: from rop import read_only_properties

In [2]: @read_only_properties('a')
   ...: class Foo:
   ...:     def __init__(self, a, b):
   ...:         self.a = a
   ...:         self.b = b
   ...:         

In [3]: f=Foo('explodes', 'ok-to-overwrite')

In [4]: f.b = 5

In [5]: f.a = 'boom'
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-5-a5226072b3b4> in <module>()
----> 1 f.a = 'boom'

/home/oznt/.virtualenvs/tracker/lib/python3.5/site-packages/rop.py in __setattr__(self, name, value)
    116                     pass
    117                 else:
--> 118                     raise AttributeError("Can't touch {}".format(name))
    119 
    120                 super().__setattr__(name, value)

AttributeError: Can't touch a

Here is a way to avoid the assumption that

all users are consenting adults, and thus are responsible for using things correctly themselves.

please see my update below

Using @property, is very verbose e.g.:

   class AClassWithManyAttributes:
        '''refactored to properties'''
        def __init__(a, b, c, d, e ...)
             self._a = a
             self._b = b
             self._c = c
             self.d = d
             self.e = e

        @property
        def a(self):
            return self._a
        @property
        def b(self):
            return self._b
        @property
        def c(self):
            return self._c
        # you get this ... it's long

Using

No underscore: it’s a public variable.
One underscore: it’s a protected variable.
Two underscores: it’s a private variable.

Except the last one, it’s a convention. You can still, if you really try hard, access variables with double underscore.

So what do we do? Do we give up on having read only properties in Python?

Behold! read_only_properties decorator to the rescue!

@read_only_properties('readonly', 'forbidden')
class MyClass(object):
    def __init__(self, a, b, c):
        self.readonly = a
        self.forbidden = b
        self.ok = c

m = MyClass(1, 2, 3)
m.ok = 4
# we can re-assign a value to m.ok
# read only access to m.readonly is OK 
print(m.ok, m.readonly) 
print("This worked...")
# this will explode, and raise AttributeError
m.forbidden = 4

You ask:

Where is read_only_properties coming from?

Glad you asked, here is the source for read_only_properties:

def read_only_properties(*attrs):

    def class_rebuilder(cls):
        "The class decorator"

        class NewClass(cls):
            "This is the overwritten class"
            def __setattr__(self, name, value):
                if name not in attrs:
                    pass
                elif name not in self.__dict__:
                    pass
                else:
                    raise AttributeError("Can't modify {}".format(name))

                super().__setattr__(name, value)
        return NewClass
    return class_rebuilder

update

I never expected this answer will get so much attention. Surprisingly it does. This encouraged me to create a package you can use.

$ pip install read-only-properties

in your python shell:

In [1]: from rop import read_only_properties

In [2]: @read_only_properties('a')
   ...: class Foo:
   ...:     def __init__(self, a, b):
   ...:         self.a = a
   ...:         self.b = b
   ...:         

In [3]: f=Foo('explodes', 'ok-to-overwrite')

In [4]: f.b = 5

In [5]: f.a = 'boom'
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-5-a5226072b3b4> in <module>()
----> 1 f.a = 'boom'

/home/oznt/.virtualenvs/tracker/lib/python3.5/site-packages/rop.py in __setattr__(self, name, value)
    116                     pass
    117                 else:
--> 118                     raise AttributeError("Can't touch {}".format(name))
    119 
    120                 super().__setattr__(name, value)

AttributeError: Can't touch a

回答 3

这是一种对只读属性略有不同的方法,由于必须对它们进行初始化,因此应该将它们称为一次写入属性,不是吗?对于那些担心直接通过访问对象字典来修改属性的偏执狂,我引入了“极端”名称处理:

from uuid import uuid4

class Read_Only_Property:
    def __init__(self, name):
        self.name = name
        self.dict_name = uuid4().hex
        self.initialized = False

    def __get__(self, instance, cls):
        if instance is None:
            return self
        else:
            return instance.__dict__[self.dict_name]

    def __set__(self, instance, value):
        if self.initialized:
            raise AttributeError("Attempt to modify read-only property '%s'." % self.name)
        instance.__dict__[self.dict_name] = value
        self.initialized = True

class Point:
    x = Read_Only_Property('x')
    y = Read_Only_Property('y')
    def __init__(self, x, y):
        self.x = x
        self.y = y

if __name__ == '__main__':
    try:
        p = Point(2, 3)
        print(p.x, p.y)
        p.x = 9
    except Exception as e:
        print(e)

Here is a slightly different approach to read-only properties, which perhaps should be called write-once properties since they do have to get initialized, don’t they? For the paranoid among us who worry about being able to modify properties by accessing the object’s dictionary directly, I’ve introduced “extreme” name mangling:

from uuid import uuid4

class Read_Only_Property:
    def __init__(self, name):
        self.name = name
        self.dict_name = uuid4().hex
        self.initialized = False

    def __get__(self, instance, cls):
        if instance is None:
            return self
        else:
            return instance.__dict__[self.dict_name]

    def __set__(self, instance, value):
        if self.initialized:
            raise AttributeError("Attempt to modify read-only property '%s'." % self.name)
        instance.__dict__[self.dict_name] = value
        self.initialized = True

class Point:
    x = Read_Only_Property('x')
    y = Read_Only_Property('y')
    def __init__(self, x, y):
        self.x = x
        self.y = y

if __name__ == '__main__':
    try:
        p = Point(2, 3)
        print(p.x, p.y)
        p.x = 9
    except Exception as e:
        print(e)

回答 4

我对创建只读属性的前两个答案不满意,因为第一个解决方案允许删除readonly属性,然后进行设置,并且不会阻止__dict__。第二种解决方案可以与测试一起解决-找到等于您将其设置为2的值并最终进行更改。

现在,获取代码。

def final(cls):
    clss = cls
    @classmethod
    def __init_subclass__(cls, **kwargs):
        raise TypeError("type '{}' is not an acceptable base type".format(clss.__name__))
    cls.__init_subclass__ = __init_subclass__
    return cls


def methoddefiner(cls, method_name):
    for clss in cls.mro():
        try:
            getattr(clss, method_name)
            return clss
        except(AttributeError):
            pass
    return None


def readonlyattributes(*attrs):
    """Method to create readonly attributes in a class

    Use as a decorator for a class. This function takes in unlimited 
    string arguments for names of readonly attributes and returns a
    function to make the readonly attributes readonly. 

    The original class's __getattribute__, __setattr__, and __delattr__ methods
    are redefined so avoid defining those methods in the decorated class

    You may create setters and deleters for readonly attributes, however
    if they are overwritten by the subclass, they lose access to the readonly
    attributes. 

    Any method which sets or deletes a readonly attribute within
    the class loses access if overwritten by the subclass besides the __new__
    or __init__ constructors.

    This decorator doesn't support subclassing of these classes
    """
    def classrebuilder(cls):
        def __getattribute__(self, name):
            if name == '__dict__':
                    from types import MappingProxyType
                    return MappingProxyType(super(cls, self).__getattribute__('__dict__'))
            return super(cls, self).__getattribute__(name)
        def __setattr__(self, name, value): 
                if name == '__dict__' or name in attrs:
                    import inspect
                    stack = inspect.stack()
                    try:
                        the_class = stack[1][0].f_locals['self'].__class__
                    except(KeyError):
                        the_class = None
                    the_method = stack[1][0].f_code.co_name
                    if the_class != cls: 
                         if methoddefiner(type(self), the_method) != cls:
                            raise AttributeError("Cannot set readonly attribute '{}'".format(name))                        
                return super(cls, self).__setattr__(name, value)
        def __delattr__(self, name):                
                if name == '__dict__' or name in attrs:
                    import inspect
                    stack = inspect.stack()
                    try:
                        the_class = stack[1][0].f_locals['self'].__class__
                    except(KeyError):
                        the_class = None
                    the_method = stack[1][0].f_code.co_name
                    if the_class != cls:
                        if methoddefiner(type(self), the_method) != cls:
                            raise AttributeError("Cannot delete readonly attribute '{}'".format(name))                        
                return super(cls, self).__delattr__(name)
        clss = cls
        cls.__getattribute__ = __getattribute__
        cls.__setattr__ = __setattr__
        cls.__delattr__ = __delattr__
        #This line will be moved when this algorithm will be compatible with inheritance
        cls = final(cls)
        return cls
    return classrebuilder

def setreadonlyattributes(cls, *readonlyattrs):
    return readonlyattributes(*readonlyattrs)(cls)


if __name__ == '__main__':
    #test readonlyattributes only as an indpendent module
    @readonlyattributes('readonlyfield')
    class ReadonlyFieldClass(object):
        def __init__(self, a, b):
            #Prevent initalization of the internal, unmodified PrivateFieldClass
            #External PrivateFieldClass can be initalized
            self.readonlyfield = a
            self.publicfield = b


    attr = None
    def main():
        global attr
        pfi = ReadonlyFieldClass('forbidden', 'changable')
        ###---test publicfield, ensure its mutable---###
        try:
            #get publicfield
            print(pfi.publicfield)
            print('__getattribute__ works')
            #set publicfield
            pfi.publicfield = 'mutable'
            print('__setattr__ seems to work')
            #get previously set publicfield
            print(pfi.publicfield)
            print('__setattr__ definitely works')
            #delete publicfield
            del pfi.publicfield 
            print('__delattr__ seems to work')
            #get publicfield which was supposed to be deleted therefore should raise AttributeError
            print(pfi.publlicfield)
            #publicfield wasn't deleted, raise RuntimeError
            raise RuntimeError('__delattr__ doesn\'t work')
        except(AttributeError):
            print('__delattr__ works')


        try:
            ###---test readonly, make sure its readonly---###
            #get readonlyfield
            print(pfi.readonlyfield)
            print('__getattribute__ works')
            #set readonlyfield, should raise AttributeError
            pfi.readonlyfield = 'readonly'
            #apparently readonlyfield was set, notify user
            raise RuntimeError('__setattr__ doesn\'t work')
        except(AttributeError):
            print('__setattr__ seems to work')
            try:
                #ensure readonlyfield wasn't set
                print(pfi.readonlyfield)
                print('__setattr__ works')
                #delete readonlyfield
                del pfi.readonlyfield
                #readonlyfield was deleted, raise RuntimeError
                raise RuntimeError('__delattr__ doesn\'t work')
            except(AttributeError):
                print('__delattr__ works')
        try:
            print("Dict testing")
            print(pfi.__dict__, type(pfi.__dict__))
            attr = pfi.readonlyfield
            print(attr)
            print("__getattribute__ works")
            if pfi.readonlyfield != 'forbidden':
                print(pfi.readonlyfield)
                raise RuntimeError("__getattr__ doesn't work")
            try:
                pfi.__dict__ = {}
                raise RuntimeError("__setattr__ doesn't work")
            except(AttributeError):
                print("__setattr__ works")
            del pfi.__dict__
            raise RuntimeError("__delattr__ doesn't work")
        except(AttributeError):
            print(pfi.__dict__)
            print("__delattr__ works")
            print("Basic things work")


main()

除非您编写库代码时使用只读属性,否则将这些属性设置为只读代码是为了增强他们的程序而将代码分发给其他人使用,而不是用于其他目的(例如应用程序开发)的代码。解决了__dict__问题,因为__dict__现在是不可变的类型。MappingProxyType,因此无法通过__dict__更改属性。设置或删除__dict__也被阻止。更改只读属性的唯一方法是更改​​类本身的方法。

尽管我认为我的解决方案比前两个解决方案要好,但可以改进。这些是此代码的弱点:

a)不允许在子类中添加设置或删除只读属性的方法。即使调用了超类的方法,也会自动禁止子类中定义的方法访问只读属性。

b)可以更改类的只读方法以克服只读限制。

但是,没有办法不编辑类来设置或删除只读属性。这不依赖于命名约定,这很好,因为Python与命名约定不太一致。这提供了一种方法,使只读属性无法通过隐藏的漏洞进行更改,而无需编辑类本身。只需在将装饰器作为参数调用时列出要只读的属性即可,它们将变为只读。

归功于Brice的回答:如何在python中另一个类的函数中获取调用方类名称?获取调用方的类和方法。

I am dissatisfied with the previous two answers to create read only properties because the first solution allows the readonly attribute to be deleted and then set and doesn’t block the __dict__. The second solution could be worked around with testing – finding the value that equals what you set it two and changing it eventually.

Now, for the code.

def final(cls):
    clss = cls
    @classmethod
    def __init_subclass__(cls, **kwargs):
        raise TypeError("type '{}' is not an acceptable base type".format(clss.__name__))
    cls.__init_subclass__ = __init_subclass__
    return cls


def methoddefiner(cls, method_name):
    for clss in cls.mro():
        try:
            getattr(clss, method_name)
            return clss
        except(AttributeError):
            pass
    return None


def readonlyattributes(*attrs):
    """Method to create readonly attributes in a class

    Use as a decorator for a class. This function takes in unlimited 
    string arguments for names of readonly attributes and returns a
    function to make the readonly attributes readonly. 

    The original class's __getattribute__, __setattr__, and __delattr__ methods
    are redefined so avoid defining those methods in the decorated class

    You may create setters and deleters for readonly attributes, however
    if they are overwritten by the subclass, they lose access to the readonly
    attributes. 

    Any method which sets or deletes a readonly attribute within
    the class loses access if overwritten by the subclass besides the __new__
    or __init__ constructors.

    This decorator doesn't support subclassing of these classes
    """
    def classrebuilder(cls):
        def __getattribute__(self, name):
            if name == '__dict__':
                    from types import MappingProxyType
                    return MappingProxyType(super(cls, self).__getattribute__('__dict__'))
            return super(cls, self).__getattribute__(name)
        def __setattr__(self, name, value): 
                if name == '__dict__' or name in attrs:
                    import inspect
                    stack = inspect.stack()
                    try:
                        the_class = stack[1][0].f_locals['self'].__class__
                    except(KeyError):
                        the_class = None
                    the_method = stack[1][0].f_code.co_name
                    if the_class != cls: 
                         if methoddefiner(type(self), the_method) != cls:
                            raise AttributeError("Cannot set readonly attribute '{}'".format(name))                        
                return super(cls, self).__setattr__(name, value)
        def __delattr__(self, name):                
                if name == '__dict__' or name in attrs:
                    import inspect
                    stack = inspect.stack()
                    try:
                        the_class = stack[1][0].f_locals['self'].__class__
                    except(KeyError):
                        the_class = None
                    the_method = stack[1][0].f_code.co_name
                    if the_class != cls:
                        if methoddefiner(type(self), the_method) != cls:
                            raise AttributeError("Cannot delete readonly attribute '{}'".format(name))                        
                return super(cls, self).__delattr__(name)
        clss = cls
        cls.__getattribute__ = __getattribute__
        cls.__setattr__ = __setattr__
        cls.__delattr__ = __delattr__
        #This line will be moved when this algorithm will be compatible with inheritance
        cls = final(cls)
        return cls
    return classrebuilder

def setreadonlyattributes(cls, *readonlyattrs):
    return readonlyattributes(*readonlyattrs)(cls)


if __name__ == '__main__':
    #test readonlyattributes only as an indpendent module
    @readonlyattributes('readonlyfield')
    class ReadonlyFieldClass(object):
        def __init__(self, a, b):
            #Prevent initalization of the internal, unmodified PrivateFieldClass
            #External PrivateFieldClass can be initalized
            self.readonlyfield = a
            self.publicfield = b


    attr = None
    def main():
        global attr
        pfi = ReadonlyFieldClass('forbidden', 'changable')
        ###---test publicfield, ensure its mutable---###
        try:
            #get publicfield
            print(pfi.publicfield)
            print('__getattribute__ works')
            #set publicfield
            pfi.publicfield = 'mutable'
            print('__setattr__ seems to work')
            #get previously set publicfield
            print(pfi.publicfield)
            print('__setattr__ definitely works')
            #delete publicfield
            del pfi.publicfield 
            print('__delattr__ seems to work')
            #get publicfield which was supposed to be deleted therefore should raise AttributeError
            print(pfi.publlicfield)
            #publicfield wasn't deleted, raise RuntimeError
            raise RuntimeError('__delattr__ doesn\'t work')
        except(AttributeError):
            print('__delattr__ works')


        try:
            ###---test readonly, make sure its readonly---###
            #get readonlyfield
            print(pfi.readonlyfield)
            print('__getattribute__ works')
            #set readonlyfield, should raise AttributeError
            pfi.readonlyfield = 'readonly'
            #apparently readonlyfield was set, notify user
            raise RuntimeError('__setattr__ doesn\'t work')
        except(AttributeError):
            print('__setattr__ seems to work')
            try:
                #ensure readonlyfield wasn't set
                print(pfi.readonlyfield)
                print('__setattr__ works')
                #delete readonlyfield
                del pfi.readonlyfield
                #readonlyfield was deleted, raise RuntimeError
                raise RuntimeError('__delattr__ doesn\'t work')
            except(AttributeError):
                print('__delattr__ works')
        try:
            print("Dict testing")
            print(pfi.__dict__, type(pfi.__dict__))
            attr = pfi.readonlyfield
            print(attr)
            print("__getattribute__ works")
            if pfi.readonlyfield != 'forbidden':
                print(pfi.readonlyfield)
                raise RuntimeError("__getattr__ doesn't work")
            try:
                pfi.__dict__ = {}
                raise RuntimeError("__setattr__ doesn't work")
            except(AttributeError):
                print("__setattr__ works")
            del pfi.__dict__
            raise RuntimeError("__delattr__ doesn't work")
        except(AttributeError):
            print(pfi.__dict__)
            print("__delattr__ works")
            print("Basic things work")


main()

There is no point to making read only attributes except when your writing library code, code which is being distributed to others as code to use in order to enhance their programs, not code for any other purpose, like app development. The __dict__ problem is solved, because the __dict__ is now of the immutable types.MappingProxyType, so attributes cannot be changed through the __dict__. Setting or deleting __dict__ is also blocked. The only way to change read only properties is through changing the methods of the class itself.

Though I believe my solution is better than of the previous two, it could be improved. These are this code’s weaknesses:

a) Doesn’t allow adding to a method in a subclass which sets or deletes a readonly attribute. A method defined in a subclass is automatically barred from accessing a readonly attribute, even by calling the superclass’ version of the method.

b) The class’ readonly methods can be changed to defeat the read only restrictions.

However, there is not way without editing the class to set or delete a read only attribute. This isn’t dependent on naming conventions, which is good because Python isn’t so consistent with naming conventions. This provides a way to make read only attributes that cannot be changed with hidden loopholes without editing the class itself. Simply list the attributes to be read only when calling the decorator as arguments and they will become read only.

Credit to Brice’s answer in How to get the caller class name inside a function of another class in python? for getting the caller classes and methods.


回答 5

注意,实例方法也是(类的)属性,如果您确实想成为坏蛋,则可以在类或实例级别设置它们。或者,您可以设置一个类变量(这也是该类的一个属性),在该变量中,方便的只读属性将无法立即使用。我要说的是,“只读属性”问题实际上比通常认为的要普遍得多。幸运的是,人们对工作的传统期望是如此强烈,以至于使我们在其他情况下视而不见(毕竟,几乎所有东西都是python中的某种属性)。

基于这些期望,我认为最通用,最轻便的方法是采用以下约定:“公开”(无前导下划线)属性是只读的,除非明确记录为可写。这包含了通常的期望,即不会对方法进行修补,而指示实例默认值的类变量则更不用说了。如果您真的对某些特殊属性感到偏执,请使用只读描述符作为最后的资源度量。

Notice that instance methods are also attributes (of the class) and that you could set them at the class or instance level if you really wanted to be a badass. Or that you may set a class variable (which is also an attribute of the class), where handy readonly properties won’t work neatly out of the box. What I’m trying to say is that the “readonly attribute” problem is in fact more general than it’s usually perceived to be. Fortunately there are conventional expectations at work that are so strong as to blind us wrt these other cases (after all, almost everything is an attribute of some sort in python).

Building upon these expectations I think the most general and lightweight approach is to adopt the convention that “public” (no leading underscore) attributes are readonly except when explicitly documented as writeable. This subsumes the usual expectation that methods won’t be patched and class variables indicating instance defaults are better let alone. If you feel really paranoid about some special attribute, use a readonly descriptor as a last resource measure.


回答 6

尽管我喜欢Oz123的类装饰器,但是您也可以执行以下操作,该操作使用显式类包装器和__new__以及类Factory方法,以在闭包内返回类:

class B(object):
    def __new__(cls, val):
        return cls.factory(val)

@classmethod
def factory(cls, val):
    private = {'var': 'test'}

    class InnerB(object):
        def __init__(self):
            self.variable = val
            pass

        @property
        def var(self):
            return private['var']

    return InnerB()

While I like the class decorator from Oz123, you could also do the following, which uses an explicit class wrapper and __new__ with a class Factory method returning the class within a closure:

class B(object):
    def __new__(cls, val):
        return cls.factory(val)

@classmethod
def factory(cls, val):
    private = {'var': 'test'}

    class InnerB(object):
        def __init__(self):
            self.variable = val
            pass

        @property
        def var(self):
            return private['var']

    return InnerB()

回答 7

那是我的解决方法。

@property
def language(self):
    return self._language
@language.setter
def language(self, value):
    # WORKAROUND to get a "getter-only" behavior
    # set the value only if the attribute does not exist
    try:
        if self.language == value:
            pass
        print("WARNING: Cannot set attribute \'language\'.")
    except AttributeError:
        self._language = value

That’s my workaround.

@property
def language(self):
    return self._language
@language.setter
def language(self, value):
    # WORKAROUND to get a "getter-only" behavior
    # set the value only if the attribute does not exist
    try:
        if self.language == value:
            pass
        print("WARNING: Cannot set attribute \'language\'.")
    except AttributeError:
        self._language = value

回答 8

有人提到使用代理对象,但我没有看到这样的示例,所以我最终尝试了一下,[可怜]。

/!\如果可能,请更喜欢类定义和类构造函数

这段代码可以有效地重写class.__new__(类构造函数),但在各个方面都更糟。减轻痛苦,如果可以,请不要使用此模式。

def attr_proxy(obj):
    """ Use dynamic class definition to bind obj and proxy_attrs.
        If you can extend the target class constructor that is 
        cleaner, but its not always trivial to do so.
    """
    proxy_attrs = dict()

    class MyObjAttrProxy():
        def __getattr__(self, name):
            if name in proxy_attrs:
                return proxy_attrs[name]  # overloaded

            return getattr(obj, name)  # proxy

        def __setattr__(self, name, value):
            """ note, self is not bound when overloading methods
            """
            proxy_attrs[name] = value

    return MyObjAttrProxy()


myobj = attr_proxy(Object())
setattr(myobj, 'foo_str', 'foo')

def func_bind_obj_as_self(func, self):
    def _method(*args, **kwargs):
        return func(self, *args, **kwargs)
    return _method

def mymethod(self, foo_ct):
    """ self is not bound because we aren't using object __new__
        you can write the __setattr__ method to bind a self 
        argument, or declare your functions dynamically to bind in 
        a static object reference.
    """
    return self.foo_str + foo_ct

setattr(myobj, 'foo', func_bind_obj_as_self(mymethod, myobj))

someone mentioned using a proxy object, I didn’t see an example of that so I ended up trying it out, [poorly].

/!\ Please prefer class definitions and class constructors if possible

this code is effectively re-writing class.__new__ (class constructor) except worse in every way. Save yourself the pain and do not use this pattern if you can.

def attr_proxy(obj):
    """ Use dynamic class definition to bind obj and proxy_attrs.
        If you can extend the target class constructor that is 
        cleaner, but its not always trivial to do so.
    """
    proxy_attrs = dict()

    class MyObjAttrProxy():
        def __getattr__(self, name):
            if name in proxy_attrs:
                return proxy_attrs[name]  # overloaded

            return getattr(obj, name)  # proxy

        def __setattr__(self, name, value):
            """ note, self is not bound when overloading methods
            """
            proxy_attrs[name] = value

    return MyObjAttrProxy()


myobj = attr_proxy(Object())
setattr(myobj, 'foo_str', 'foo')

def func_bind_obj_as_self(func, self):
    def _method(*args, **kwargs):
        return func(self, *args, **kwargs)
    return _method

def mymethod(self, foo_ct):
    """ self is not bound because we aren't using object __new__
        you can write the __setattr__ method to bind a self 
        argument, or declare your functions dynamically to bind in 
        a static object reference.
    """
    return self.foo_str + foo_ct

setattr(myobj, 'foo', func_bind_obj_as_self(mymethod, myobj))

回答 9

我知道我从头开始带回了这个线程,但是我正在研究如何使属性变为只读,并且在找到该主题之后,我对已经共享的解决方案不满意。

因此,如果您从以下代码开始,请回到最初的问题:

@property
def x(self):
    return self._x

并且您想将X设为只读,只需添加:

@x.setter
def x(self, value):
    raise Exception("Member readonly")

然后,如果您运行以下命令:

print (x) # Will print whatever X value is
x = 3 # Will raise exception "Member readonly"

I know i’m bringing back from the dead this thread, but I was looking at how to make a property read only and after finding this topic, I wasn’t satisfied with the solutions already shared.

So, going back to the initial question, if you start with this code:

@property
def x(self):
    return self._x

And you want to make X readonly, you can just add:

@x.setter
def x(self, value):
    raise Exception("Member readonly")

Then, if you run the following:

print (x) # Will print whatever X value is
x = 3 # Will raise exception "Member readonly"

在python中从字典设置属性

问题:在python中从字典设置属性

是否可以通过python中的字典创建对象,使得每个键都是该对象的属性?

像这样:

 d = { 'name': 'Oscar', 'lastName': 'Reyes', 'age':32 }

 e = Employee(d) 
 print e.name # Oscar 
 print e.age + 10 # 42 

我认为这与该问题几乎完全相反:来自对象字段的Python字典

Is it possible to create an object from a dictionary in python in such a way that each key is an attribute of that object?

Something like this:

 d = { 'name': 'Oscar', 'lastName': 'Reyes', 'age':32 }

 e = Employee(d) 
 print e.name # Oscar 
 print e.age + 10 # 42 

I think it would be pretty much the inverse of this question: Python dictionary from an object’s fields


回答 0

当然,是这样的:

class Employee(object):
    def __init__(self, initial_data):
        for key in initial_data:
            setattr(self, key, initial_data[key])

更新资料

正如布伦特·纳什(Brent Nash)所建议的,您还可以通过允许使用关键字参数来使其更加灵活:

class Employee(object):
    def __init__(self, *initial_data, **kwargs):
        for dictionary in initial_data:
            for key in dictionary:
                setattr(self, key, dictionary[key])
        for key in kwargs:
            setattr(self, key, kwargs[key])

然后您可以这样称呼它:

e = Employee({"name": "abc", "age": 32})

或像这样:

e = Employee(name="abc", age=32)

甚至像这样:

employee_template = {"role": "minion"}
e = Employee(employee_template, name="abc", age=32)

Sure, something like this:

class Employee(object):
    def __init__(self, initial_data):
        for key in initial_data:
            setattr(self, key, initial_data[key])

Update

As Brent Nash suggests, you can make this more flexible by allowing keyword arguments as well:

class Employee(object):
    def __init__(self, *initial_data, **kwargs):
        for dictionary in initial_data:
            for key in dictionary:
                setattr(self, key, dictionary[key])
        for key in kwargs:
            setattr(self, key, kwargs[key])

Then you can call it like this:

e = Employee({"name": "abc", "age": 32})

or like this:

e = Employee(name="abc", age=32)

or even like this:

employee_template = {"role": "minion"}
e = Employee(employee_template, name="abc", age=32)

回答 1

以这种方式设置属性几乎肯定不是解决问题的最佳方法。要么:

  1. 您知道所有字段都应该提前。在这种情况下,您可以显式设置所有属性。这看起来像

    class Employee(object):
        def __init__(self, name, last_name, age):
            self.name = name
            self.last_name = last_name
            self.age = age
    
    d = {'name': 'Oscar', 'last_name': 'Reyes', 'age':32 }
    e = Employee(**d) 
    
    print e.name # Oscar 
    print e.age + 10 # 42 

    要么

  2. 您不知道所有字段都应该提前。在这种情况下,您应该将数据存储为dict,而不是污染对象命名空间。这些属性用于静态访问。这种情况看起来像

    class Employee(object):
        def __init__(self, data):
            self.data = data
    
    d = {'name': 'Oscar', 'last_name': 'Reyes', 'age':32 }
    e = Employee(d) 
    
    print e.data['name'] # Oscar 
    print e.data['age'] + 10 # 42 

与情况1基本等效的另一种解决方案是使用collections.namedtuple。有关如何实现的信息,请参见van的答案。

Setting attributes in this way is almost certainly not the best way to solve a problem. Either:

  1. You know what all the fields should be ahead of time. In that case, you can set all the attributes explicitly. This would look like

    class Employee(object):
        def __init__(self, name, last_name, age):
            self.name = name
            self.last_name = last_name
            self.age = age
    
    d = {'name': 'Oscar', 'last_name': 'Reyes', 'age':32 }
    e = Employee(**d) 
    
    print e.name # Oscar 
    print e.age + 10 # 42 
    

    or

  2. You don’t know what all the fields should be ahead of time. In this case, you should store the data as a dict instead of polluting an objects namespace. Attributes are for static access. This case would look like

    class Employee(object):
        def __init__(self, data):
            self.data = data
    
    d = {'name': 'Oscar', 'last_name': 'Reyes', 'age':32 }
    e = Employee(d) 
    
    print e.data['name'] # Oscar 
    print e.data['age'] + 10 # 42 
    

Another solution that is basically equivalent to case 1 is to use a collections.namedtuple. See van’s answer for how to implement that.


回答 2

您可以使用访问对象的属性__dict__,并对其调用update方法:

>>> class Employee(object):
...     def __init__(self, _dict):
...         self.__dict__.update(_dict)
... 


>>> dict = { 'name': 'Oscar', 'lastName': 'Reyes', 'age':32 }

>>> e = Employee(dict)

>>> e.name
'Oscar'

>>> e.age
32

You can access the attributes of an object with __dict__, and call the update method on it:

>>> class Employee(object):
...     def __init__(self, _dict):
...         self.__dict__.update(_dict)
... 


>>> dict = { 'name': 'Oscar', 'lastName': 'Reyes', 'age':32 }

>>> e = Employee(dict)

>>> e.name
'Oscar'

>>> e.age
32

回答 3

为什么不只使用属性名称作为字典的键?

class StructMyDict(dict):

     def __getattr__(self, name):
         try:
             return self[name]
         except KeyError as e:
             raise AttributeError(e)

     def __setattr__(self, name, value):
         self[name] = value

您可以使用命名参数,元组列表,字典或单独的属性分配进行初始化,例如:

nautical = StructMyDict(left = "Port", right = "Starboard") # named args

nautical2 = StructMyDict({"left":"Port","right":"Starboard"}) # dictionary

nautical3 = StructMyDict([("left","Port"),("right","Starboard")]) # tuples list

nautical4 = StructMyDict()  # fields TBD
nautical4.left = "Port"
nautical4.right = "Starboard"

for x in [nautical, nautical2, nautical3, nautical4]:
    print "%s <--> %s" % (x.left,x.right)

或者,您可以为未知值返回None,而不是引发属性错误。(web2py存储类中使用的一个技巧)

Why not just use attribute names as keys to a dictionary?

class StructMyDict(dict):

     def __getattr__(self, name):
         try:
             return self[name]
         except KeyError as e:
             raise AttributeError(e)

     def __setattr__(self, name, value):
         self[name] = value

You can initialize with named arguments, a list of tuples, or a dictionary, or individual attribute assignments, e.g.:

nautical = StructMyDict(left = "Port", right = "Starboard") # named args

nautical2 = StructMyDict({"left":"Port","right":"Starboard"}) # dictionary

nautical3 = StructMyDict([("left","Port"),("right","Starboard")]) # tuples list

nautical4 = StructMyDict()  # fields TBD
nautical4.left = "Port"
nautical4.right = "Starboard"

for x in [nautical, nautical2, nautical3, nautical4]:
    print "%s <--> %s" % (x.left,x.right)

Alternatively, instead of raising the attribute error, you can return None for unknown values. (A trick used in the web2py storage class)


回答 4

我认为,settattr如果您确实需要支持,那么使用答案是可行的方法dict

但是,如果Employeeobject只是可以使用点语法(.name)而不是dict语法(['name'])访问的结构,则可以使用namedtuple,如下所示:

from collections import namedtuple

Employee = namedtuple('Employee', 'name age')
e = Employee('noname01', 6)
print e
#>> Employee(name='noname01', age=6)

# create Employee from dictionary
d = {'name': 'noname02', 'age': 7}
e = Employee(**d)
print e
#>> Employee(name='noname02', age=7)
print e._asdict()
#>> {'age': 7, 'name': 'noname02'}

您确实具有_asdict()将所有属性作为字典访问的方法,但是以后只能在构造过程中才能添加其他属性。

I think that answer using settattr are the way to go if you really need to support dict.

But if Employee object is just a structure which you can access with dot syntax (.name) instead of dict syntax (['name']), you can use namedtuple like this:

from collections import namedtuple

Employee = namedtuple('Employee', 'name age')
e = Employee('noname01', 6)
print e
#>> Employee(name='noname01', age=6)

# create Employee from dictionary
d = {'name': 'noname02', 'age': 7}
e = Employee(**d)
print e
#>> Employee(name='noname02', age=7)
print e._asdict()
#>> {'age': 7, 'name': 'noname02'}

You do have _asdict() method to access all properties as dictionary, but you cannot add additional attributes later, only during the construction.


回答 5

例如说

class A():
    def __init__(self):
        self.x=7
        self.y=8
        self.z="name"

如果您想一次设置属性

d = {'x':100,'y':300,'z':"blah"}
a = A()
a.__dict__.update(d)

say for example

class A():
    def __init__(self):
        self.x=7
        self.y=8
        self.z="name"

if you want to set the attributes at once

d = {'x':100,'y':300,'z':"blah"}
a = A()
a.__dict__.update(d)

回答 6

与使用dict类似,您可以像这样使用kwargs:

class Person:
   def __init__(self, **kwargs):
       self.properties = kwargs

   def get_property(self, key):
       return self.properties.get(key, None)

   def main():
       timmy = Person(color = 'red')
       print(timmy.get_property('color')) #prints 'red'

similar to using a dict, you could just use kwargs like so:

class Person:
   def __init__(self, **kwargs):
       self.properties = kwargs

   def get_property(self, key):
       return self.properties.get(key, None)

   def main():
       timmy = Person(color = 'red')
       print(timmy.get_property('color')) #prints 'red'

模块可以具有与对象相同的属性吗?

问题:模块可以具有与对象相同的属性吗?

使用python属性,我可以做到

obj.y 

调用一个函数,而不只是返回一个值。

有没有办法用模块来做到这一点?我有一个想要的情况

module.y 

调用一个函数,而不仅仅是返回存储在那里的值。

With python properties, I can make it such that

obj.y 

calls a function rather than just returning a value.

Is there a way to do this with modules? I have a case where I want

module.y 

to call a function, rather than just returning the value stored there.


回答 0

只有新式类的实例才能具有属性。通过将其存储在中,可以使Python相信这样的实例是模块sys.modules[thename] = theinstance。因此,例如,您的m.py模块文件可能是:

import sys

class _M(object):
    def __init__(self):
        self.c = 0
    def afunction(self):
        self.c += 1
        return self.c
    y = property(afunction)

sys.modules[__name__] = _M()

Only instances of new-style classes can have properties. You can make Python believe such an instance is a module by stashing it in sys.modules[thename] = theinstance. So, for example, your m.py module file could be:

import sys

class _M(object):
    def __init__(self):
        self.c = 0
    def afunction(self):
        self.c += 1
        return self.c
    y = property(afunction)

sys.modules[__name__] = _M()

回答 1

我这样做是为了正确继承模块的所有属性,并由isinstance()正确识别

import types

class MyModule(types.ModuleType):
    @property
    def y(self):
        return 5


>>> a=MyModule("test")
>>> a
<module 'test' (built-in)>
>>> a.y
5

然后可以将其插入sys.modules中:

sys.modules[__name__] = MyModule(__name__)  # remember to instantiate the class

I would do this in order to properly inherit all the attributes of a module, and be correctly identified by isinstance()

import types

class MyModule(types.ModuleType):
    @property
    def y(self):
        return 5


>>> a=MyModule("test")
>>> a
<module 'test' (built-in)>
>>> a.y
5

And then you can insert this into sys.modules:

sys.modules[__name__] = MyModule(__name__)  # remember to instantiate the class

回答 2

由于PEP 562已在Python> = 3.7中实现,现在我们可以执行此操作

文件:module.py

def __getattr__(name):
    if name == 'y':
        return 3
    raise AttributeError(f"module '{__name__}' has no attribute '{name}'")

other = 4

用法:

>>> import module
>>> module.y
3
>>> module.other
4
>>> module.nosuch
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "module.py", line 4, in __getattr__
    raise AttributeError(f"module '{__name__}' has no attribute '{name}'")
AttributeError: module 'module' has no attribute 'nosuch'

请注意,如果您raise AttributeError__getattr__函数中省略,则表示函数以结束return None,则的module.nosuch值将为None

As PEP 562 has been implemented in Python >= 3.7, now we can do this

file: module.py

def __getattr__(name):
    if name == 'y':
        return 3
    raise AttributeError(f"module '{__name__}' has no attribute '{name}'")

other = 4

usage:

>>> import module
>>> module.y
3
>>> module.other
4
>>> module.nosuch
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "module.py", line 4, in __getattr__
    raise AttributeError(f"module '{__name__}' has no attribute '{name}'")
AttributeError: module 'module' has no attribute 'nosuch'

Note that if you omit the raise AttributeError in the __getattr__ function, it means the function ends with return None, then the module.nosuch will get a value of None.


回答 3

根据约翰·林的回答

def module_property(func):
    """Decorator to turn module functions into properties.
    Function names must be prefixed with an underscore."""
    module = sys.modules[func.__module__]

    def base_getattr(name):
        raise AttributeError(
            f"module '{module.__name__}' has no attribute '{name}'")

    old_getattr = getattr(module, '__getattr__', base_getattr)

    def new_getattr(name):
        if f'_{name}' == func.__name__:
            return func()
        else:
            return old_getattr(name)

    module.__getattr__ = new_getattr
    return func

用法(请注意下划线),位于the_module.py

@module_property
def _thing():
    return 'hello'

然后:

import the_module

print(the_module.thing)  # prints 'hello'

前导下划线对于将属性化功能与原始功能区分开是必要的。我想不出一种重新分配标识符的方法,因为在执行装饰器的过程中,尚未分配它。

请注意,IDE不会知道该属性存在,并且会显示红色波形。

Based on John Lin’s answer:

def module_property(func):
    """Decorator to turn module functions into properties.
    Function names must be prefixed with an underscore."""
    module = sys.modules[func.__module__]

    def base_getattr(name):
        raise AttributeError(
            f"module '{module.__name__}' has no attribute '{name}'")

    old_getattr = getattr(module, '__getattr__', base_getattr)

    def new_getattr(name):
        if f'_{name}' == func.__name__:
            return func()
        else:
            return old_getattr(name)

    module.__getattr__ = new_getattr
    return func

Usage (note the leading underscore), in the_module.py:

@module_property
def _thing():
    return 'hello'

Then:

import the_module

print(the_module.thing)  # prints 'hello'

The leading underscore is necessary to differentiate the property-ized function from the original function. I couldn’t think of a way to reassign the identifier, since during the time of the decorator execution, it has not been assigned yet.

Note that IDEs won’t know that the property exists and will show red wavies.


回答 4

一个典型的用例是:使用一些动态属性来丰富(庞大的)现有模块-而无需将所有模块内容转换为类布局。不幸的是,最简单的模块类补丁如sys.modules[__name__].__class__ = MyPropertyModule失败了TypeError: __class__ assignment: only for heap types。因此,需要重新创建模块。

这种方法无需使用Python导入钩子就能做到这一点,只需在模块代码的顶部添加一些序言即可:

# propertymodule.py
""" Module property example """

if '__orgmod__' not in globals():

    # constant prolog for having module properties / supports reload()

    print "PropertyModule stub execution", __name__
    import sys, types
    class PropertyModule(types.ModuleType):
        def __str__(self):
            return "<PropertyModule %r from %r>" % (self.__name__, self.__file__)
    modnew = PropertyModule(__name__, __doc__)
    modnew.__modclass__ = PropertyModule        
    modnew.__file__ = __file__
    modnew.__orgmod__ = sys.modules[__name__]
    sys.modules[__name__] = modnew
    exec sys._getframe().f_code in modnew.__dict__

else:

    # normal module code (usually vast) ..

    print "regular module execution"
    a = 7

    def get_dynval(module):
        return "property function returns %s in module %r" % (a * 4, module.__name__)    
    __modclass__.dynval = property(get_dynval)

用法:

>>> import propertymodule
PropertyModule stub execution propertymodule
regular module execution
>>> propertymodule.dynval
"property function returns 28 in module 'propertymodule'"
>>> reload(propertymodule)   # AFTER EDITS
regular module execution
<module 'propertymodule' from 'propertymodule.pyc'>
>>> propertymodule.dynval
"property function returns 36 in module 'propertymodule'"

注意:类似的东西from propertymodule import dynval会产生一个冻结的过程,当然-与dynval = someobject.dynval

A typical use case is: enriching a (huge) existing module with some (few) dynamic attributes – without turning all module stuff into a class layout. Unfortunately a most simple module class patch like sys.modules[__name__].__class__ = MyPropertyModule fails with TypeError: __class__ assignment: only for heap types. So module creation needs to be rewired.

This approach does it without Python import hooks, just by having some prolog on top of the module code:

# propertymodule.py
""" Module property example """

if '__orgmod__' not in globals():

    # constant prolog for having module properties / supports reload()

    print "PropertyModule stub execution", __name__
    import sys, types
    class PropertyModule(types.ModuleType):
        def __str__(self):
            return "<PropertyModule %r from %r>" % (self.__name__, self.__file__)
    modnew = PropertyModule(__name__, __doc__)
    modnew.__modclass__ = PropertyModule        
    modnew.__file__ = __file__
    modnew.__orgmod__ = sys.modules[__name__]
    sys.modules[__name__] = modnew
    exec sys._getframe().f_code in modnew.__dict__

else:

    # normal module code (usually vast) ..

    print "regular module execution"
    a = 7

    def get_dynval(module):
        return "property function returns %s in module %r" % (a * 4, module.__name__)    
    __modclass__.dynval = property(get_dynval)

Usage:

>>> import propertymodule
PropertyModule stub execution propertymodule
regular module execution
>>> propertymodule.dynval
"property function returns 28 in module 'propertymodule'"
>>> reload(propertymodule)   # AFTER EDITS
regular module execution
<module 'propertymodule' from 'propertymodule.pyc'>
>>> propertymodule.dynval
"property function returns 36 in module 'propertymodule'"

Note: Something like from propertymodule import dynval will produce a frozen copy of course – corresponding to dynval = someobject.dynval


回答 5

一个简单的答案:使用 proxy_tools

proxy_tools软件包尝试提供@module_property功能。

它安装与

pip install proxy_tools

在@Marein的示例中,the_module.py我们 稍作修改

from proxy_tools import module_property

@module_property
def thing():
    print(". ", end='')  # Prints ". " on each invocation
    return 'hello'

现在,从另一个脚本中,我可以

import the_module

print(the_module.thing)
# . hello

意外行为

这个解决方案并非没有警告。也就是说,the_module.thing不是字符串!这是一个proxy_tools.Proxy对象,其特殊方法已被覆盖,因此它模仿字符串。这是一些说明点的基本测试:

res = the_module.thing
# [No output!!! Evaluation doesn't occur yet.]

print(type(res))
# <class 'proxy_tools.Proxy'>

print(isinstance(res, str))
# False

print(res)
# . hello

print(res + " there")
# . hello there

print(isinstance(res + "", str))
# . True

print(res.split('e'))
# . ['h', 'llo']

在内部,原始函数存储在 the_module.thing._Proxy__local

print(res._Proxy__local)
# <function thing at 0x7f729c3bf680>

进一步的想法

老实说,我对为什么模块没有内置此功能感到困惑。我认为问题的关键在于这the_moduletypes.ModuleType类的实例。设置“模块属性”等于在此类的实例上而不是在types.ModuleType类本身上设置属性。有关更多详细信息,请参见此答案

types.ModuleType尽管结果不是很好,但实际上我们可以按如下方式实现属性。我们不能直接修改内置类型,但是可以诅咒它们:

# python -m pip install forbiddenfruit
from forbiddenfruit import curse
from types import ModuleType
# curse has the same signature as setattr.
curse(ModuleType, "thing2", property(lambda module: f'hi from {module.__name__}'))

这为我们提供了一个存在于所有模块上的属性。这有点荒谬,因为我们打破了所有模块的设置行为:

import sys

print(sys.thing2)
# hi from sys

sys.thing2 = 5
# AttributeError: can't set attribute

A short answer: use proxy_tools

The proxy_tools package attempts to provide @module_property functionality.

It installs with

pip install proxy_tools

Using a slight modification of @Marein’s example, in the_module.py we put

from proxy_tools import module_property

@module_property
def thing():
    print(". ", end='')  # Prints ". " on each invocation
    return 'hello'

Now from another script, I can do

import the_module

print(the_module.thing)
# . hello

Unexpected behavior

This solution is not without caveats. Namely, the_module.thing is not a string! It is a proxy_tools.Proxy object whose special methods have been overridden so that it mimicks a string. Here are some basic tests which illustrate the point:

res = the_module.thing
# [No output!!! Evaluation doesn't occur yet.]

print(type(res))
# <class 'proxy_tools.Proxy'>

print(isinstance(res, str))
# False

print(res)
# . hello

print(res + " there")
# . hello there

print(isinstance(res + "", str))
# . True

print(res.split('e'))
# . ['h', 'llo']

Internally, the original function is stored to the_module.thing._Proxy__local:

print(res._Proxy__local)
# <function thing at 0x7f729c3bf680>

Further thoughts

Honestly, I’m baffled about why modules don’t have this functionality built in. I think the crux of the matter is that the_module is an instance of the types.ModuleType class. Setting a “module property” amounts to setting a property on an instance of this class, rather than on the types.ModuleType class itself. For more details, see this answer.

We can actually implement properties on types.ModuleType as follows, although the results are not great. We can’t directly modify built-in types, but we can curse them:

# python -m pip install forbiddenfruit
from forbiddenfruit import curse
from types import ModuleType
# curse has the same signature as setattr.
curse(ModuleType, "thing2", property(lambda module: f'hi from {module.__name__}'))

This gives us a property which exists over all modules. It’s a bit unwieldly, since we break the setting behavior across all modules:

import sys

print(sys.thing2)
# hi from sys

sys.thing2 = 5
# AttributeError: can't set attribute

关于如何在python中使用属性功能的真实示例?

问题:关于如何在python中使用属性功能的真实示例?

我对如何@property在Python中使用感兴趣。我阅读了python文档,并认为其中的示例只是一个玩具代码:

class C(object):
    def __init__(self):
        self._x = None

    @property
    def x(self):
        """I'm the 'x' property."""
        return self._x

    @x.setter
    def x(self, value):
        self._x = value

    @x.deleter
    def x(self):
        del self._x

我不知道通过包装_x用属性装饰器填充的内容可以获得什么好处。为什么不只是实现为:

class C(object):
    def __init__(self):
        self.x = None

我认为,属性功能在某些情况下可能很有用。但当?有人可以给我一些真实的例子吗?

谢谢。

I am interested in how to use @property in Python. I’ve read the python docs and the example there, in my opinion, is just a toy code:

class C(object):
    def __init__(self):
        self._x = None

    @property
    def x(self):
        """I'm the 'x' property."""
        return self._x

    @x.setter
    def x(self, value):
        self._x = value

    @x.deleter
    def x(self):
        del self._x

I do not know what benefit(s) I can get from wrapping the _x filled with the property decorator. Why not just implement as:

class C(object):
    def __init__(self):
        self.x = None

I think, the property feature might be useful in some situations. But when? Could someone please give me some real-world examples?

Thanks.


回答 0

其他示例是对设置的属性进行验证/过滤(强制将它们限制或接受)以及对复杂或快速变化的术语进行惰性评估。

隐藏在属性后面的复杂计算:

class PDB_Calculator(object):
    ...
    @property
    def protein_folding_angle(self):
        # number crunching, remote server calls, etc
        # all results in an angle set in 'some_angle'
        # It could also reference a cache, remote or otherwise,
        # that holds the latest value for this angle
        return some_angle

>>> f = PDB_Calculator()
>>> angle = f.protein_folding_angle
>>> angle
44.33276

验证:

class Pedometer(object)
    ...
    @property
    def stride_length(self):
        return self._stride_length

    @stride_length.setter
    def stride_length(self, value):
        if value > 10:
            raise ValueError("This pedometer is based on the human stride - a stride length above 10m is not supported")
        else:
            self._stride_length = value

Other examples would be validation/filtering of the set attributes (forcing them to be in bounds or acceptable) and lazy evaluation of complex or rapidly changing terms.

Complex calculation hidden behind an attribute:

class PDB_Calculator(object):
    ...
    @property
    def protein_folding_angle(self):
        # number crunching, remote server calls, etc
        # all results in an angle set in 'some_angle'
        # It could also reference a cache, remote or otherwise,
        # that holds the latest value for this angle
        return some_angle

>>> f = PDB_Calculator()
>>> angle = f.protein_folding_angle
>>> angle
44.33276

Validation:

class Pedometer(object)
    ...
    @property
    def stride_length(self):
        return self._stride_length

    @stride_length.setter
    def stride_length(self, value):
        if value > 10:
            raise ValueError("This pedometer is based on the human stride - a stride length above 10m is not supported")
        else:
            self._stride_length = value

回答 1

一个简单的用例是设置一个只读实例属性,因为您知道_x在python 中用一个下划线开头一个变量名通常意味着它是私有的(内部使用),但是有时我们希望能够读取实例属性而不是编写所以我们可以使用property它:

>>> class C(object):

        def __init__(self, x):
            self._x = x

        @property
        def x(self):
            return self._x

>>> c = C(1)
>>> c.x
1
>>> c.x = 2
AttributeError        Traceback (most recent call last)

AttributeError: can't set attribute

One simple use case will be to set a read only instance attribute , as you know leading a variable name with one underscore _x in python usually mean it’s private (internal use) but sometimes we want to be able to read the instance attribute and not to write it so we can use property for this:

>>> class C(object):

        def __init__(self, x):
            self._x = x

        @property
        def x(self):
            return self._x

>>> c = C(1)
>>> c.x
1
>>> c.x = 2
AttributeError        Traceback (most recent call last)

AttributeError: can't set attribute

回答 2

看一下这篇文章,将其用于非常实际的用途。简而言之,它解释了在Python中通常如何放弃显式的getter / setter方法,因为如果在某个阶段需要它们,则可以使用它property来实现无缝实现。

Take a look at this article for a very practical use. In short, it explains how in Python you can usually ditch explicit getter/setter method, since if you come to need them at some stage you can use property for a seamless implementation.


回答 3

我使用它的一件事是缓存存储在数据库中的查找缓慢但不变的值。这会泛化到您的属性需要计算或您只想按需执行的其他长时间操作(例如数据库检查,网络通信)的任何情况。

class Model(object):

  def get_a(self):
    if not hasattr(self, "_a"):
      self._a = self.db.lookup("a")
    return self._a

  a = property(get_a)

这是在一个Web应用程序中,其中任何给定的页面视图可能只需要一个这样的特定属性,但是基础对象本身可能具有几个这样的属性-在构造时将它们全部初始化将是浪费的,而属性使我可以灵活地使用属性是惰性的,而不是。

One thing I’ve used it for is caching slow-to-look-up, but unchanging, values stored in a database. This generalises to any situation where your attributes require computation or some other long operation (eg. database check, network communication) which you only want to do on demand.

class Model(object):

  def get_a(self):
    if not hasattr(self, "_a"):
      self._a = self.db.lookup("a")
    return self._a

  a = property(get_a)

This was in a web app where any given page view might only need one particular attribute of this kind, but the underlying objects themselves might have several such attributes – initialising them all on construction would be wasteful, and properties allow me to be flexible in which attributes are lazy and which aren’t.


回答 4

通读答案和评论,主题似乎是答案似乎缺少一个简单但有用的示例。我在这里包括了一个非常简单的示例,演示了@property装饰器的简单用法。该类允许用户使用各种不同的单位(即in_feet或)指定并获取距离测量值in_metres

class Distance(object):
    def __init__(self):
        # This private attribute will store the distance in metres
        # All units provided using setters will be converted before
        # being stored
        self._distance = 0.0

    @property
    def in_metres(self):
        return self._distance

    @in_metres.setter
    def in_metres(self, val):
        try:
            self._distance = float(val)
        except:
            raise ValueError("The input you have provided is not recognised "
                             "as a valid number")

    @property
    def in_feet(self):
        return self._distance * 3.2808399

    @in_feet.setter
    def in_feet(self, val):
        try:
            self._distance = float(val) / 3.2808399
        except:
            raise ValueError("The input you have provided is not recognised "
                             "as a valid number")

    @property
    def in_parsecs(self):
        return self._distance * 3.24078e-17

    @in_parsecs.setter
    def in_parsecs(self, val):
        try:
            self._distance = float(val) / 3.24078e-17
        except:
            raise ValueError("The input you have provided is not recognised "
                             "as a valid number")

用法:

>>> distance = Distance()
>>> distance.in_metres = 1000.0
>>> distance.in_metres
1000.0
>>> distance.in_feet
3280.8399
>>> distance.in_parsecs
3.24078e-14

Reading through the answers and comments, the main theme seems to be the answers seem to be missing a simple, yet useful example. I have included a very simple one here that demonstrates the simple use of the @property decorator. It’s a class that allows a user to specify and get distance measurement using a variety of different units, i.e. in_feet or in_metres.

class Distance(object):
    def __init__(self):
        # This private attribute will store the distance in metres
        # All units provided using setters will be converted before
        # being stored
        self._distance = 0.0

    @property
    def in_metres(self):
        return self._distance

    @in_metres.setter
    def in_metres(self, val):
        try:
            self._distance = float(val)
        except:
            raise ValueError("The input you have provided is not recognised "
                             "as a valid number")

    @property
    def in_feet(self):
        return self._distance * 3.2808399

    @in_feet.setter
    def in_feet(self, val):
        try:
            self._distance = float(val) / 3.2808399
        except:
            raise ValueError("The input you have provided is not recognised "
                             "as a valid number")

    @property
    def in_parsecs(self):
        return self._distance * 3.24078e-17

    @in_parsecs.setter
    def in_parsecs(self, val):
        try:
            self._distance = float(val) / 3.24078e-17
        except:
            raise ValueError("The input you have provided is not recognised "
                             "as a valid number")

Usage:

>>> distance = Distance()
>>> distance.in_metres = 1000.0
>>> distance.in_metres
1000.0
>>> distance.in_feet
3280.8399
>>> distance.in_parsecs
3.24078e-14

回答 5

属性只是字段的抽象,它使您可以更好地控制特定字段的操作方式和中间件计算。想到的用法很少是验证,事先初始化和访问限制

@property
def x(self):
    """I'm the 'x' property."""
    if self._x is None:
        self._x = Foo()

    return self._x

Property is just an abstraction around a field which give you more control on ways that a specific field can be manipulated and to do middleware computations. Few of the usages that come to mind is validation and prior initialization and access restriction

@property
def x(self):
    """I'm the 'x' property."""
    if self._x is None:
        self._x = Foo()

    return self._x

回答 6

是的,对于发布的原始示例,该属性的作用与仅具有实例变量“ x”的作用完全相同。

这是关于python属性的最好的事情。从外部看,它们的工作方式完全类似于实例变量!这允许您从类外部使用实例变量。

这意味着您的第一个示例实际上可以使用实例变量。如果情况发生了变化,然后您决定更改实现,并且一个属性很有用,则该类的代码与该类外部的代码的接口将相同。 从实例变量更改为属性不会影响类外部的代码。

许多其他语言和编程类将指示程序员不要公开实例变量,而应使用“ getters”和“ setters”从类外部访问任何值,即使是问题中引用的简单情况也是如此。

类外的代码使用多种语言(例如Java)

object.get_i()
    #and
object.set_i(value)

#in place of (with python)
object.i
    #and 
object.i = value

在实现该类时,有许多“ getter”和“ setter”的作用与您的第一个示例完全相同:复制一个简单的实例变量。这些获取器和设置器是必需的,因为如果类实现更改,则该类外部的所有代码都需要更改。但是python属性允许类外的代码与实例变量相同。因此,如果添加属性或具有简单的实例变量,则无需更改类外部的代码。因此,与大多数面向对象的语言不同,对于您的简单示例,您可以使用实例变量代替真正不需要的“ getters”和“ setters”,因此请确保在将来更改为属性时,可以使用您的Class无需更改。

这意味着仅当行为复杂时才需要创建属性,对于非常普遍的简单情况(如问题中所述),仅需要一个简单的实例变量,您只需使用实例变量即可。

Yes, for the original example posted, the property will work exactly the same as simply having an instance variable ‘x’.

This is the best thing about python properties. From the outside, they work exactly like instance variables! Which allows you to use instance variables from outside the class.

This means your first example could actually use an instance variable. If things changed, and then you decide to change your implementation and a property is useful, the interface to the property would still be the same from code outside the class. A change from instance variable to property has no impact on code outside the class.

Many other languages and programming courses will instruct that a programmer should never expose instance variables, and instead use ‘getters’ and ‘setters’ for any value to be accessed from outside the class, even the simple case as quoted in the question.

Code outside the class with many languages (e.g. Java) use

object.get_i()
    #and
object.set_i(value)

#in place of (with python)
object.i
    #and 
object.i = value

And when implementing the class there are many ‘getters’ and ‘setters’ that do exactly as your first example: replicate a simply instance variable. These getters and setters are required because if the class implementation changes, all the code outside the class will need to change. But python properties allow code outside the class to be the same as with instance variables. So code outside the class does not need to be changed if you add a property, or have a simple instance variable. So unlike most Object Oriented languages, for your simple example you can use the instance variable instead of ‘getters’ and ‘setters’ that are really not needed, secure in the knowledge that if you change to a property in the future, the code using your class need not change.

This means you only need create properties if there is complex behaviour, and for the very common simple case where, as described in the question, a simple instance variable is all that is needed, you can just use the instance variable.


回答 7

与使用setter和getters相比,属性的另一个不错的功能是,它们允许您继续在属性上使用OP =运算符(例如,+ =,-=,* =等),同时仍保留任何验证,访问控制,缓存等设置者和获取者将提供。

例如,如果您Person使用setter setage(newage)和getter 编写了类,getage()则要增加年龄,您必须编写:

bob = Person('Robert', 25)
bob.setage(bob.getage() + 1)

但是如果您age有财产,您可以写得更加简洁:

bob.age += 1

another nice feature of properties over using setters and getters it that they allow you to continue to use OP= operators (eg +=, -=, *= etc) on your attributes while still retaining any validation, access control, caching, etc that the setters and getters would supply.

for example if you wrote the class Person with a setter setage(newage), and a getter getage(), then to increment the age you would have to write:

bob = Person('Robert', 25)
bob.setage(bob.getage() + 1)

but if you made age a property you could write the much cleaner:

bob.age += 1

回答 8

这个问题的简短答案是,在您的示例中,没有任何好处。您可能应该使用不包含属性的表格。

属性存在的原因是,如果您的代码在将来发生更改,并且您突然需要对数据做更多的事情:缓存值,保护访问,查询一些外部资源…等等,您可以轻松地修改类以添加getter和数据的设置器,而无需更改接口,因此您不必在代码中的任何地方找到要访问该数据的地方,也不必进行更改。

The short answer to your question, is that in your example, there is no benefit. You should probably use the form that doesn’t involve properties.

The reason properties exists, is that if your code changes in the future, and you suddenly need to do more with your data: cache values, protect access, query some external resource… whatever, you can easily modify your class to add getters and setters for the data without changing the interface, so you don’t have to find everywhere in your code where that data is accessed and change that too.


回答 9

起初许多人没有注意到的事情是您可以创建自己的属性子类。我发现这对于公开只读对象属性或可以读写但不能删除的属性非常有用。这也是包装诸如跟踪对对象字段的修改之类的功能的绝佳方法。

class reader(property):
    def __init__(self, varname):
        _reader = lambda obj: getattr(obj, varname)
        super(reader, self).__init__(_reader)

class accessor(property):
    def __init__(self, varname, set_validation=None):
        _reader = lambda obj: getattr(obj, varname)
        def _writer(obj, value):
            if set_validation is not None:
               if set_validation(value):
                  setattr(obj, varname, value)
        super(accessor, self).__init__(_reader, _writer)

#example
class MyClass(object):
   def __init__(self):
     self._attr = None

   attr = reader('_attr')

Something that many do not notice at first is that you can make your own subclasses of property. This I have found very useful for exposing read only object attributes or attribute you can read and write but not remove. It is also an excellent way to wrap functionality like tracking modifications to object fields.

class reader(property):
    def __init__(self, varname):
        _reader = lambda obj: getattr(obj, varname)
        super(reader, self).__init__(_reader)

class accessor(property):
    def __init__(self, varname, set_validation=None):
        _reader = lambda obj: getattr(obj, varname)
        def _writer(obj, value):
            if set_validation is not None:
               if set_validation(value):
                  setattr(obj, varname, value)
        super(accessor, self).__init__(_reader, _writer)

#example
class MyClass(object):
   def __init__(self):
     self._attr = None

   attr = reader('_attr')

如何动态地向类添加属性?

问题:如何动态地向类添加属性?

目标是创建一个行为类似db结果集的模拟类。

因此,例如,如果数据库查询使用dict表达式返回{'ab':100, 'cd':200},那么我想看看:

>>> dummy.ab
100

刚开始我以为我可以这样做:

ks = ['ab', 'cd']
vs = [12, 34]
class C(dict):
    def __init__(self, ks, vs):
        for i, k in enumerate(ks):
            self[k] = vs[i]
            setattr(self, k, property(lambda x: vs[i], self.fn_readyonly))

    def fn_readonly(self, v)
        raise "It is ready only"

if __name__ == "__main__":
    c = C(ks, vs)
    print c.ab

c.ab返回一个属性对象。

用替换该setattrk = property(lambda x: vs[i])根本没有用。

那么在运行时创建实例属性的正确方法是什么?

PS我知道如何使用__getattribute__方法?

The goal is to create a mock class which behaves like a db resultset.

So for example, if a database query returns, using a dict expression, {'ab':100, 'cd':200}, then I would like to see:

>>> dummy.ab
100

At first I thought maybe I could do it this way:

ks = ['ab', 'cd']
vs = [12, 34]
class C(dict):
    def __init__(self, ks, vs):
        for i, k in enumerate(ks):
            self[k] = vs[i]
            setattr(self, k, property(lambda x: vs[i], self.fn_readyonly))

    def fn_readonly(self, v)
        raise "It is ready only"

if __name__ == "__main__":
    c = C(ks, vs)
    print c.ab

but c.ab returns a property object instead.

Replacing the setattr line with k = property(lambda x: vs[i]) is of no use at all.

So what is the right way to create an instance property at runtime?

P.S. I am aware of an alternative presented in How is the __getattribute__ method used?


回答 0

我想我应该扩大这个答案,因为我年龄较大,比较聪明,并且知道发生了什么事。迟到总比不到好。

可以动态地向类添加属性。但这很重要:您必须将其添加到类中

>>> class Foo(object):
...     pass
... 
>>> foo = Foo()
>>> foo.a = 3
>>> Foo.b = property(lambda self: self.a + 1)
>>> foo.b
4

property实际上,A 是称为描述符的事物的简单实现。该对象为给定类的给定属性提供自定义处理。Kinda就像一种从if树上拔出一棵大树的方法__getattribute__

当我要求foo.b在上面的例子中,Python看到的是,b在类实现限定的描述符协议哪位只是意味着它是一个对象__get____set____delete__方法。描述符声明负责处理该属性,因此Python调用Foo.b.__get__(foo, Foo),并将返回值作为属性值传递回给您。在的情况下property,每一种方法只是调用fgetfsetfdel你传递给property构造函数。

描述符实际上是Python公开其整个OO实现的基本方法。实际上,还有一种比更为常见的描述符property

>>> class Foo(object):
...     def bar(self):
...         pass
... 
>>> Foo().bar
<bound method Foo.bar of <__main__.Foo object at 0x7f2a439d5dd0>>
>>> Foo().bar.__get__
<method-wrapper '__get__' of instancemethod object at 0x7f2a43a8a5a0>

谦虚方法只是另一种描述符。它__get__以调用实例为第一个参数;实际上,它是这样做的:

def __get__(self, instance, owner):
    return functools.partial(self.function, instance)

无论如何,我怀疑这就是为什么描述符仅在类上起作用的原因:它们是首先为类提供支持的东西的形式化形式。它们甚至是规则的exceptions:您显然可以为一个类分配描述符,而类本身就是type!的实例。实际上,试图读取Foo.b静态调用property.__get__;当描述符作为类属性访问时,返回它们自己只是惯用的。

我认为几乎所有Python的OO系统都可以用Python表示非常酷。:)

哦,如果您有兴趣的话,我写了一篇关于描述符冗长博客文章

I suppose I should expand this answer, now that I’m older and wiser and know what’s going on. Better late than never.

You can add a property to a class dynamically. But that’s the catch: you have to add it to the class.

>>> class Foo(object):
...     pass
... 
>>> foo = Foo()
>>> foo.a = 3
>>> Foo.b = property(lambda self: self.a + 1)
>>> foo.b
4

A property is actually a simple implementation of a thing called a descriptor. It’s an object that provides custom handling for a given attribute, on a given class. Kinda like a way to factor a huge if tree out of __getattribute__.

When I ask for foo.b in the example above, Python sees that the b defined on the class implements the descriptor protocol—which just means it’s an object with a __get__, __set__, or __delete__ method. The descriptor claims responsibility for handling that attribute, so Python calls Foo.b.__get__(foo, Foo), and the return value is passed back to you as the value of the attribute. In the case of property, each of these methods just calls the fget, fset, or fdel you passed to the property constructor.

Descriptors are really Python’s way of exposing the plumbing of its entire OO implementation. In fact, there’s another type of descriptor even more common than property.

>>> class Foo(object):
...     def bar(self):
...         pass
... 
>>> Foo().bar
<bound method Foo.bar of <__main__.Foo object at 0x7f2a439d5dd0>>
>>> Foo().bar.__get__
<method-wrapper '__get__' of instancemethod object at 0x7f2a43a8a5a0>

The humble method is just another kind of descriptor. Its __get__ tacks on the calling instance as the first argument; in effect, it does this:

def __get__(self, instance, owner):
    return functools.partial(self.function, instance)

Anyway, I suspect this is why descriptors only work on classes: they’re a formalization of the stuff that powers classes in the first place. They’re even the exception to the rule: you can obviously assign descriptors to a class, and classes are themselves instances of type! In fact, trying to read Foo.b still calls property.__get__; it’s just idiomatic for descriptors to return themselves when accessed as class attributes.

I think it’s pretty cool that virtually all of Python’s OO system can be expressed in Python. :)

Oh, and I wrote a wordy blog post about descriptors a while back if you’re interested.


回答 1

目标是创建一个行为类似db结果集的模拟类。

因此,您需要一本可以将a [‘b’]拼写为ab的字典吗?

这很简单:

class atdict(dict):
    __getattr__= dict.__getitem__
    __setattr__= dict.__setitem__
    __delattr__= dict.__delitem__

The goal is to create a mock class which behaves like a db resultset.

So what you want is a dictionary where you can spell a[‘b’] as a.b?

That’s easy:

class atdict(dict):
    __getattr__= dict.__getitem__
    __setattr__= dict.__setitem__
    __delattr__= dict.__delitem__

回答 2

似乎您可以使用来更简单地解决此问题namedtuple,因为您提前知道了整个字段列表。

from collections import namedtuple

Foo = namedtuple('Foo', ['bar', 'quux'])

foo = Foo(bar=13, quux=74)
print foo.bar, foo.quux

foo2 = Foo()  # error

如果您绝对需要编写自己的setter,则必须在类级别进行元编程。property()在实例上不起作用。

It seems you could solve this problem much more simply with a namedtuple, since you know the entire list of fields ahead of time.

from collections import namedtuple

Foo = namedtuple('Foo', ['bar', 'quux'])

foo = Foo(bar=13, quux=74)
print foo.bar, foo.quux

foo2 = Foo()  # error

If you absolutely need to write your own setter, you’ll have to do the metaprogramming at the class level; property() doesn’t work on instances.


回答 3

您不需要为此使用属性。只需重写__setattr__即可使其只读。

class C(object):
    def __init__(self, keys, values):
        for (key, value) in zip(keys, values):
            self.__dict__[key] = value

    def __setattr__(self, name, value):
        raise Exception("It is read only!")

多田

>>> c = C('abc', [1,2,3])
>>> c.a
1
>>> c.b
2
>>> c.c
3
>>> c.d
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'C' object has no attribute 'd'
>>> c.d = 42
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 6, in __setattr__
Exception: It is read only!
>>> c.a = 'blah'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 6, in __setattr__
Exception: It is read only!

You don’t need to use a property for that. Just override __setattr__ to make them read only.

class C(object):
    def __init__(self, keys, values):
        for (key, value) in zip(keys, values):
            self.__dict__[key] = value

    def __setattr__(self, name, value):
        raise Exception("It is read only!")

Tada.

>>> c = C('abc', [1,2,3])
>>> c.a
1
>>> c.b
2
>>> c.c
3
>>> c.d
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'C' object has no attribute 'd'
>>> c.d = 42
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 6, in __setattr__
Exception: It is read only!
>>> c.a = 'blah'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 6, in __setattr__
Exception: It is read only!

回答 4

如何动态地向python类添加属性?

假设您有一个对象要添加属性。通常,当我需要开始管理对具有下游用法的代码中的属性的访问时,我想使用属性,以便可以维护一致的API。现在,我通常将它们添加到定义对象的源代码中,但是让我们假设您没有该访问权限,或者您需要以编程方式真正地动态选择函数。

建立类

使用基于的文档property的示例,让我们创建一个具有“隐藏”属性的对象类并创建一个实例:

class C(object):
    '''basic class'''
    _x = None

o = C()

在Python中,我们期望有一种显而易见的处理方式。但是,在这种情况下,我将展示两种方式:使用装饰符表示法和不使用装饰符表示法。首先,没有装饰符号。这对于动态获取,设置或删除程序可能更为有用。

动态(又名Monkey修补)

让我们为我们的Class创建一些:

def getx(self):
    return self._x

def setx(self, value):
    self._x = value

def delx(self):
    del self._x

现在,我们将这些分配给属性。请注意,我们可以在此处以编程方式选择函数,回答动态问题:

C.x = property(getx, setx, delx, "I'm the 'x' property.")

和用法:

>>> o.x = 'foo'
>>> o.x
'foo'
>>> del o.x
>>> print(o.x)
None
>>> help(C.x)
Help on property:

    I'm the 'x' property.

装饰工

我们可以使用装饰器符号做与上面相同的操作,但是在这种情况下,我们必须将所有方法都命名为相同的名称(我建议将其与属性保持相同),因此,程序化赋值并不是那么简单它使用上面的方法:

@property
def x(self):
    '''I'm the 'x' property.'''
    return self._x

@x.setter
def x(self, value):
    self._x = value

@x.deleter
def x(self):
    del self._x

并将属性对象及其提供的设置器和删除器分配给该类:

C.x = x

和用法:

>>> help(C.x)
Help on property:

    I'm the 'x' property.

>>> o.x
>>> o.x = 'foo'
>>> o.x
'foo'
>>> del o.x
>>> print(o.x)
None

How to add property to a python class dynamically?

Say you have an object that you want to add a property to. Typically, I want to use properties when I need to begin managing access to an attribute in code that has downstream usage, so that I can maintain a consistent API. Now I will typically add them to the source code where the object is defined, but let’s assume you don’t have that access, or you need to truly dynamically choose your functions programmatically.

Create a class

Using an example based on the documentation for property, let’s create a class of object with a “hidden” attribute and create an instance of it:

class C(object):
    '''basic class'''
    _x = None

o = C()

In Python, we expect there to be one obvious way of doing things. However, in this case, I’m going to show two ways: with decorator notation, and without. First, without decorator notation. This may be more useful for the dynamic assignment of getters, setters, or deleters.

Dynamic (a.k.a. Monkey Patching)

Let’s create some for our class:

def getx(self):
    return self._x

def setx(self, value):
    self._x = value

def delx(self):
    del self._x

And now we assign these to the property. Note that we could choose our functions programmatically here, answering the dynamic question:

C.x = property(getx, setx, delx, "I'm the 'x' property.")

And usage:

>>> o.x = 'foo'
>>> o.x
'foo'
>>> del o.x
>>> print(o.x)
None
>>> help(C.x)
Help on property:

    I'm the 'x' property.

Decorators

We could do the same as we did above with decorator notation, but in this case, we must name the methods all the same name (and I’d recommend keeping it the same as the attribute), so programmatic assignment is not so trivial as it is using the above method:

@property
def x(self):
    '''I'm the 'x' property.'''
    return self._x

@x.setter
def x(self, value):
    self._x = value

@x.deleter
def x(self):
    del self._x

And assign the property object with its provisioned setters and deleters to the class:

C.x = x

And usage:

>>> help(C.x)
Help on property:

    I'm the 'x' property.

>>> o.x
>>> o.x = 'foo'
>>> o.x
'foo'
>>> del o.x
>>> print(o.x)
None

回答 5

在这个Stack Overflow帖子上问了一个相似的问题以创建一个创建简单类型的类工厂。结果是这个答案具有Class工厂的工作版本。这是答案的片段:

def Struct(*args, **kwargs):
    def init(self, *iargs, **ikwargs):
        for k,v in kwargs.items():
            setattr(self, k, v)
        for i in range(len(iargs)):
            setattr(self, args[i], iargs[i])
        for k,v in ikwargs.items():
            setattr(self, k, v)

    name = kwargs.pop("name", "MyStruct")
    kwargs.update(dict((k, None) for k in args))
    return type(name, (object,), {'__init__': init, '__slots__': kwargs.keys()})

>>> Person = Struct('fname', 'age')
>>> person1 = Person('Kevin', 25)
>>> person2 = Person(age=42, fname='Terry')
>>> person1.age += 10
>>> person2.age -= 10
>>> person1.fname, person1.age, person2.fname, person2.age
('Kevin', 35, 'Terry', 32)
>>>

您可以使用此方法的一些变体来创建默认值,这是您的目标(该问题中也有一个答案与此相关)。

I asked a similary question on this Stack Overflow post to create a class factory which created simple types. The outcome was this answer which had a working version of the class factory. Here is a snippet of the answer:

def Struct(*args, **kwargs):
    def init(self, *iargs, **ikwargs):
        for k,v in kwargs.items():
            setattr(self, k, v)
        for i in range(len(iargs)):
            setattr(self, args[i], iargs[i])
        for k,v in ikwargs.items():
            setattr(self, k, v)

    name = kwargs.pop("name", "MyStruct")
    kwargs.update(dict((k, None) for k in args))
    return type(name, (object,), {'__init__': init, '__slots__': kwargs.keys()})

>>> Person = Struct('fname', 'age')
>>> person1 = Person('Kevin', 25)
>>> person2 = Person(age=42, fname='Terry')
>>> person1.age += 10
>>> person2.age -= 10
>>> person1.fname, person1.age, person2.fname, person2.age
('Kevin', 35, 'Terry', 32)
>>>

You could use some variation of this to create default values which is your goal (there is also an answer in that question which deals with this).


回答 6

不知道我是否完全理解这个问题,但是您可以在运行时使用类的内置实例属性进行修改__dict__

class C(object):
    def __init__(self, ks, vs):
        self.__dict__ = dict(zip(ks, vs))


if __name__ == "__main__":
    ks = ['ab', 'cd']
    vs = [12, 34]
    c = C(ks, vs)
    print(c.ab) # 12

Not sure if I completely understand the question, but you can modify instance properties at runtime with the built-in __dict__ of your class:

class C(object):
    def __init__(self, ks, vs):
        self.__dict__ = dict(zip(ks, vs))


if __name__ == "__main__":
    ks = ['ab', 'cd']
    vs = [12, 34]
    c = C(ks, vs)
    print(c.ab) # 12

回答 7

对于那些来自搜索引擎的人来说,这是我在谈论动态属性时正在寻找的两件事:

class Foo:
    def __init__(self):
        # we can dynamically have access to the properties dict using __dict__
        self.__dict__['foo'] = 'bar'

assert Foo().foo == 'bar'


# or we can use __getattr__ and __setattr__ to execute code on set/get
class Bar:
    def __init__(self):
        self._data = {}
    def __getattr__(self, key):
        return self._data[key]
    def __setattr__(self, key, value):
        self._data[key] = value

bar = Bar()
bar.foo = 'bar'
assert bar.foo == 'bar'

__dict__如果要放置动态创建的属性,则很好。__getattr__仅在需要该值时才做某事是很好的,例如查询数据库。设置/获取组合可以简化对类中存储的数据的访问(就像上面的示例一样)。

如果只需要一个动态属性,请查看property()内置函数。

For those coming from search engines, here are the two things I was looking for when talking about dynamic properties:

class Foo:
    def __init__(self):
        # we can dynamically have access to the properties dict using __dict__
        self.__dict__['foo'] = 'bar'

assert Foo().foo == 'bar'


# or we can use __getattr__ and __setattr__ to execute code on set/get
class Bar:
    def __init__(self):
        self._data = {}
    def __getattr__(self, key):
        return self._data[key]
    def __setattr__(self, key, value):
        self._data[key] = value

bar = Bar()
bar.foo = 'bar'
assert bar.foo == 'bar'

__dict__ is good if you want to put dynamically created properties. __getattr__ is good to only do something when the value is needed, like query a database. The set/get combo is good to simplify the access to data stored in the class (like in the example above).

If you only want one dynamic property, have a look at the property() built-in function.


回答 8

您不能property()在运行时向实例添加新实例,因为属性是数据描述符。相反,您必须动态创建一个新类或重载__getattribute__以处理实例上的数据描述符。

You cannot add a new property() to an instance at runtime, because properties are data descriptors. Instead you must dynamically create a new class, or overload __getattribute__ in order to process data descriptors on instances.


回答 9

最好的方法是通过定义__slots__。这样,您的实例就不能具有新属性。

ks = ['ab', 'cd']
vs = [12, 34]

class C(dict):
    __slots__ = []
    def __init__(self, ks, vs): self.update(zip(ks, vs))
    def __getattr__(self, key): return self[key]

if __name__ == "__main__":
    c = C(ks, vs)
    print c.ab

那打印 12

    c.ab = 33

这给出了: AttributeError: 'C' object has no attribute 'ab'

The best way to achieve is by defining __slots__. That way your instances can’t have new attributes.

ks = ['ab', 'cd']
vs = [12, 34]

class C(dict):
    __slots__ = []
    def __init__(self, ks, vs): self.update(zip(ks, vs))
    def __getattr__(self, key): return self[key]

if __name__ == "__main__":
    c = C(ks, vs)
    print c.ab

That prints 12

    c.ab = 33

That gives: AttributeError: 'C' object has no attribute 'ab'


回答 10

另一个例子,如何达到预期的效果

class Foo(object):

    _bar = None

    @property
    def bar(self):
        return self._bar

    @bar.setter
    def bar(self, value):
        self._bar = value

    def __init__(self, dyn_property_name):
        setattr(Foo, dyn_property_name, Foo.bar)

所以现在我们可以做类似的事情:

>>> foo = Foo('baz')
>>> foo.baz = 5
>>> foo.bar
5
>>> foo.baz
5

Just another example how to achieve desired effect

class Foo(object):

    _bar = None

    @property
    def bar(self):
        return self._bar

    @bar.setter
    def bar(self, value):
        self._bar = value

    def __init__(self, dyn_property_name):
        setattr(Foo, dyn_property_name, Foo.bar)

So now we can do stuff like:

>>> foo = Foo('baz')
>>> foo.baz = 5
>>> foo.bar
5
>>> foo.baz
5

回答 11

这是一个解决方案:

  • 允许将属性名称指定为string,因此它们可以来自某些外部数据源,而不是在程序中列出的所有名称。
  • 在定义类时添加属性,而不是在每次创建对象时添加属性

定义了类之后,只需执行以下操作即可向其动态添加属性:

setattr(SomeClass, 'propertyName', property(getter, setter))

这是一个经过Python 3测试的完整示例:

#!/usr/bin/env python3

class Foo():
  pass

def get_x(self):
  return 3

def set_x(self, value):
  print("set x on %s to %d" % (self, value))

setattr(Foo, 'x', property(get_x, set_x))

foo1 = Foo()
foo1.x = 12
print(foo1.x)

Here is a solution that:

  • Allows specifying property names as strings, so they can come from some outside data source instead of all being listed in your program.
  • Adds the properties when the class is defined, instead of every time an object is created.

After the class has been defined, you just do this to add a property to it dynamically:

setattr(SomeClass, 'propertyName', property(getter, setter))

Here is a complete example, tested in Python 3:

#!/usr/bin/env python3

class Foo():
  pass

def get_x(self):
  return 3

def set_x(self, value):
  print("set x on %s to %d" % (self, value))

setattr(Foo, 'x', property(get_x, set_x))

foo1 = Foo()
foo1.x = 12
print(foo1.x)

回答 12

您可以使用以下代码通过字典对象更新类属性:

class ExampleClass():
    def __init__(self, argv):
        for key, val in argv.items():
            self.__dict__[key] = val

if __name__ == '__main__':
    argv = {'intro': 'Hello World!'}
    instance = ExampleClass(argv)
    print instance.intro

You can use the following code to update class attributes using a dictionary object:

class ExampleClass():
    def __init__(self, argv):
        for key, val in argv.items():
            self.__dict__[key] = val

if __name__ == '__main__':
    argv = {'intro': 'Hello World!'}
    instance = ExampleClass(argv)
    print instance.intro

回答 13

这与OP想要的有所不同,但是我动了动脑筋,直到找到可行的解决方案为止,所以我在这里为下一个家伙/女友

我需要一种方法来指定动态的setter和getter。

class X:
    def __init__(self, a=0, b=0, c=0):
        self.a = a
        self.b = b
        self.c = c

    @classmethod
    def _make_properties(cls, field_name, inc):
        _inc = inc

        def _get_properties(self):
            if not hasattr(self, '_%s_inc' % field_name):
                setattr(self, '_%s_inc' % field_name, _inc)
                inc = _inc
            else:
                inc = getattr(self, '_%s_inc' % field_name)

            return getattr(self, field_name) + inc

        def _set_properties(self, value):
            setattr(self, '_%s_inc' % field_name, value)

        return property(_get_properties, _set_properties)

我提前知道我的领域,所以我要创建我的属性。注意:您不能执行此PER实例,这些属性将存在于类中!!!

for inc, field in enumerate(['a', 'b', 'c']):
    setattr(X, '%s_summed' % field, X._make_properties(field, inc))

让我们现在测试一下。

x = X()
assert x.a == 0
assert x.b == 0
assert x.c == 0

assert x.a_summed == 0  # enumerate() set inc to 0 + 0 = 0
assert x.b_summed == 1  # enumerate() set inc to 1 + 0 = 1
assert x.c_summed == 2  # enumerate() set inc to 2 + 0 = 2

# we set the variables to something
x.a = 1
x.b = 2
x.c = 3

assert x.a_summed == 1  # enumerate() set inc to 0 + 1 = 1
assert x.b_summed == 3  # enumerate() set inc to 1 + 2 = 3
assert x.c_summed == 5  # enumerate() set inc to 2 + 3 = 5

# we're changing the inc now
x.a_summed = 1 
x.b_summed = 3 
x.c_summed = 5

assert x.a_summed == 2  # we set inc to 1 + the property was 1 = 2
assert x.b_summed == 5  # we set inc to 3 + the property was 2 = 5
assert x.c_summed == 8  # we set inc to 5 + the property was 3 = 8

令人困惑吗?是的,很抱歉,我无法提出任何有意义的现实示例。同样,这也不适合那些轻松的人。

This is a little different than what OP wanted, but I rattled my brain until I got a working solution, so I’m putting here for the next guy/gal

I needed a way to specify dynamic setters and getters.

class X:
    def __init__(self, a=0, b=0, c=0):
        self.a = a
        self.b = b
        self.c = c

    @classmethod
    def _make_properties(cls, field_name, inc):
        _inc = inc

        def _get_properties(self):
            if not hasattr(self, '_%s_inc' % field_name):
                setattr(self, '_%s_inc' % field_name, _inc)
                inc = _inc
            else:
                inc = getattr(self, '_%s_inc' % field_name)

            return getattr(self, field_name) + inc

        def _set_properties(self, value):
            setattr(self, '_%s_inc' % field_name, value)

        return property(_get_properties, _set_properties)

I know my fields ahead of time so im going to create my properties. NOTE: you cannot do this PER instance, these properties will exist on the class!!!

for inc, field in enumerate(['a', 'b', 'c']):
    setattr(X, '%s_summed' % field, X._make_properties(field, inc))

Let’s test it all now..

x = X()
assert x.a == 0
assert x.b == 0
assert x.c == 0

assert x.a_summed == 0  # enumerate() set inc to 0 + 0 = 0
assert x.b_summed == 1  # enumerate() set inc to 1 + 0 = 1
assert x.c_summed == 2  # enumerate() set inc to 2 + 0 = 2

# we set the variables to something
x.a = 1
x.b = 2
x.c = 3

assert x.a_summed == 1  # enumerate() set inc to 0 + 1 = 1
assert x.b_summed == 3  # enumerate() set inc to 1 + 2 = 3
assert x.c_summed == 5  # enumerate() set inc to 2 + 3 = 5

# we're changing the inc now
x.a_summed = 1 
x.b_summed = 3 
x.c_summed = 5

assert x.a_summed == 2  # we set inc to 1 + the property was 1 = 2
assert x.b_summed == 5  # we set inc to 3 + the property was 2 = 5
assert x.c_summed == 8  # we set inc to 5 + the property was 3 = 8

Is it confusing? Yes, sorry I couldn’t come up with any meaningful real world examples. Also, this is not for the light hearted.


回答 14

这似乎可行(但请参见下文):

class data(dict,object):
    def __init__(self,*args,**argd):
        dict.__init__(self,*args,**argd)
        self.__dict__.update(self)
    def __setattr__(self,name,value):
        raise AttributeError,"Attribute '%s' of '%s' object cannot be set"%(name,self.__class__.__name__)
    def __delattr__(self,name):
        raise AttributeError,"Attribute '%s' of '%s' object cannot be deleted"%(name,self.__class__.__name__)

如果您需要更复杂的行为,请随时编辑答案。

编辑

对于大型数据集,以下内容可能会更节省内存:

class data(dict,object):
    def __init__(self,*args,**argd):
        dict.__init__(self,*args,**argd)
    def __getattr__(self,name):
        return self[name]
    def __setattr__(self,name,value):
        raise AttributeError,"Attribute '%s' of '%s' object cannot be set"%(name,self.__class__.__name__)
    def __delattr__(self,name):
        raise AttributeError,"Attribute '%s' of '%s' object cannot be deleted"%(name,self.__class__.__name__)

This seems to work(but see below):

class data(dict,object):
    def __init__(self,*args,**argd):
        dict.__init__(self,*args,**argd)
        self.__dict__.update(self)
    def __setattr__(self,name,value):
        raise AttributeError,"Attribute '%s' of '%s' object cannot be set"%(name,self.__class__.__name__)
    def __delattr__(self,name):
        raise AttributeError,"Attribute '%s' of '%s' object cannot be deleted"%(name,self.__class__.__name__)

If you need more complex behavior, feel free to edit your answer.

edit

The following would probably be more memory-efficient for large datasets:

class data(dict,object):
    def __init__(self,*args,**argd):
        dict.__init__(self,*args,**argd)
    def __getattr__(self,name):
        return self[name]
    def __setattr__(self,name,value):
        raise AttributeError,"Attribute '%s' of '%s' object cannot be set"%(name,self.__class__.__name__)
    def __delattr__(self,name):
        raise AttributeError,"Attribute '%s' of '%s' object cannot be deleted"%(name,self.__class__.__name__)

回答 15

为了回答问题的主旨,您希望将字典中的只读属性作为不可变数据源:

目标是创建一个行为类似db结果集的模拟类。

因此,例如,如果数据库查询使用dict表达式返回 {'ab':100, 'cd':200},那么我将看到

>>> dummy.ab
100

我将演示如何使用namedtuple来自collections模块的a来完成此任务:

import collections

data = {'ab':100, 'cd':200}

def maketuple(d):
    '''given a dict, return a namedtuple'''
    Tup = collections.namedtuple('TupName', d.keys()) # iterkeys in Python2
    return Tup(**d)

dummy = maketuple(data)
dummy.ab

退货 100

To answer the main thrust of your question, you want a read-only attribute from a dict as an immutable datasource:

The goal is to create a mock class which behaves like a db resultset.

So for example, if a database query returns, using a dict expression, {'ab':100, 'cd':200}, then I would to see

>>> dummy.ab
100

I’ll demonstrate how to use a namedtuple from the collections module to accomplish just this:

import collections

data = {'ab':100, 'cd':200}

def maketuple(d):
    '''given a dict, return a namedtuple'''
    Tup = collections.namedtuple('TupName', d.keys()) # iterkeys in Python2
    return Tup(**d)

dummy = maketuple(data)
dummy.ab

returns 100


回答 16

class atdict(dict):
  def __init__(self, value, **kwargs):
    super().__init__(**kwargs)
    self.__dict = value

  def __getattr__(self, name):
    for key in self.__dict:
      if type(self.__dict[key]) is list:
        for idx, item in enumerate(self.__dict[key]):
          if type(item) is dict:
            self.__dict[key][idx] = atdict(item)
      if type(self.__dict[key]) is dict:
        self.__dict[key] = atdict(self.__dict[key])
    return self.__dict[name]



d1 = atdict({'a' : {'b': [{'c': 1}, 2]}})

print(d1.a.b[0].c)

输出为:

>> 1
class atdict(dict):
  def __init__(self, value, **kwargs):
    super().__init__(**kwargs)
    self.__dict = value

  def __getattr__(self, name):
    for key in self.__dict:
      if type(self.__dict[key]) is list:
        for idx, item in enumerate(self.__dict[key]):
          if type(item) is dict:
            self.__dict[key][idx] = atdict(item)
      if type(self.__dict[key]) is dict:
        self.__dict[key] = atdict(self.__dict[key])
    return self.__dict[name]



d1 = atdict({'a' : {'b': [{'c': 1}, 2]}})

print(d1.a.b[0].c)

And the output is:

>> 1

回答 17

kjfletch扩展想法

# This is my humble contribution, extending the idea to serialize
# data from and to tuples, comparison operations and allowing functions
# as default values.

def Struct(*args, **kwargs):
    FUNCTIONS = (types.BuiltinFunctionType, types.BuiltinMethodType, \
                 types.FunctionType, types.MethodType)
    def init(self, *iargs, **ikwargs):
        """Asume that unamed args are placed in the same order than
        astuple() yields (currently alphabetic order)
        """
        kw = list(self.__slots__)

        # set the unnamed args
        for i in range(len(iargs)):
            k = kw.pop(0)
            setattr(self, k, iargs[i])

        # set the named args
        for k, v in ikwargs.items():
            setattr(self, k, v)
            kw.remove(k)

        # set default values
        for k in kw:
            v = kwargs[k]
            if isinstance(v, FUNCTIONS):
                v = v()
            setattr(self, k, v)

    def astuple(self):
        return tuple([getattr(self, k) for k in self.__slots__])

    def __str__(self):
        data = ['{}={}'.format(k, getattr(self, k)) for k in self.__slots__]
        return '<{}: {}>'.format(self.__class__.__name__, ', '.join(data))

    def __repr__(self):
        return str(self)

    def __eq__(self, other):
        return self.astuple() == other.astuple()

    name = kwargs.pop("__name__", "MyStruct")
    slots = list(args)
    slots.extend(kwargs.keys())
    # set non-specific default values to None
    kwargs.update(dict((k, None) for k in args))

    return type(name, (object,), {
        '__init__': init,
        '__slots__': tuple(slots),
        'astuple': astuple,
        '__str__': __str__,
        '__repr__': __repr__,
        '__eq__': __eq__,
    })


Event = Struct('user', 'cmd', \
               'arg1', 'arg2',  \
               date=time.time, \
               __name__='Event')

aa = Event('pepe', 77)
print(aa)
raw = aa.astuple()

bb = Event(*raw)
print(bb)

if aa == bb:
    print('Are equals')

cc = Event(cmd='foo')
print(cc)

输出:

<Event: user=pepe, cmd=77, arg1=None, arg2=None, date=1550051398.3651814>
<Event: user=pepe, cmd=77, arg1=None, arg2=None, date=1550051398.3651814>
Are equals
<Event: user=None, cmd=foo, arg1=None, arg2=None, date=1550051403.7938335>

Extending the idea from kjfletch

# This is my humble contribution, extending the idea to serialize
# data from and to tuples, comparison operations and allowing functions
# as default values.

def Struct(*args, **kwargs):
    FUNCTIONS = (types.BuiltinFunctionType, types.BuiltinMethodType, \
                 types.FunctionType, types.MethodType)
    def init(self, *iargs, **ikwargs):
        """Asume that unamed args are placed in the same order than
        astuple() yields (currently alphabetic order)
        """
        kw = list(self.__slots__)

        # set the unnamed args
        for i in range(len(iargs)):
            k = kw.pop(0)
            setattr(self, k, iargs[i])

        # set the named args
        for k, v in ikwargs.items():
            setattr(self, k, v)
            kw.remove(k)

        # set default values
        for k in kw:
            v = kwargs[k]
            if isinstance(v, FUNCTIONS):
                v = v()
            setattr(self, k, v)

    def astuple(self):
        return tuple([getattr(self, k) for k in self.__slots__])

    def __str__(self):
        data = ['{}={}'.format(k, getattr(self, k)) for k in self.__slots__]
        return '<{}: {}>'.format(self.__class__.__name__, ', '.join(data))

    def __repr__(self):
        return str(self)

    def __eq__(self, other):
        return self.astuple() == other.astuple()

    name = kwargs.pop("__name__", "MyStruct")
    slots = list(args)
    slots.extend(kwargs.keys())
    # set non-specific default values to None
    kwargs.update(dict((k, None) for k in args))

    return type(name, (object,), {
        '__init__': init,
        '__slots__': tuple(slots),
        'astuple': astuple,
        '__str__': __str__,
        '__repr__': __repr__,
        '__eq__': __eq__,
    })


Event = Struct('user', 'cmd', \
               'arg1', 'arg2',  \
               date=time.time, \
               __name__='Event')

aa = Event('pepe', 77)
print(aa)
raw = aa.astuple()

bb = Event(*raw)
print(bb)

if aa == bb:
    print('Are equals')

cc = Event(cmd='foo')
print(cc)

Output:

<Event: user=pepe, cmd=77, arg1=None, arg2=None, date=1550051398.3651814>
<Event: user=pepe, cmd=77, arg1=None, arg2=None, date=1550051398.3651814>
Are equals
<Event: user=None, cmd=foo, arg1=None, arg2=None, date=1550051403.7938335>

回答 18

尽管给出了很多答案,但我找不到我满意的答案。我想出了自己的解决方案,该解决方案property适用于动态案例。回答原始问题的来源:

#!/usr/local/bin/python3

INITS = { 'ab': 100, 'cd': 200 }

class DP(dict):
  def __init__(self):
    super().__init__()
    for k,v in INITS.items():
        self[k] = v 

def _dict_set(dp, key, value):
  dp[key] = value

for item in INITS.keys():
  setattr(
    DP,
    item,
    lambda key: property(
      lambda self: self[key], lambda self, value: _dict_set(self, key, value)
    )(item)
  )

a = DP()
print(a)  # {'ab': 100, 'cd': 200}
a.ab = 'ab100'
a.cd = False
print(a.ab, a.cd) # ab100 False

Although many answers are given, I couldn’t find one I am happy with. I figured out my own solution which makes property work for the dynamic case. The source to answer the original question:

#!/usr/local/bin/python3

INITS = { 'ab': 100, 'cd': 200 }

class DP(dict):
  def __init__(self):
    super().__init__()
    for k,v in INITS.items():
        self[k] = v 

def _dict_set(dp, key, value):
  dp[key] = value

for item in INITS.keys():
  setattr(
    DP,
    item,
    lambda key: property(
      lambda self: self[key], lambda self, value: _dict_set(self, key, value)
    )(item)
  )

a = DP()
print(a)  # {'ab': 100, 'cd': 200}
a.ab = 'ab100'
a.cd = False
print(a.ab, a.cd) # ab100 False

回答 19

对我有用的是这样的:

class C:
    def __init__(self):
        self._x=None

    def g(self):
        return self._x

    def s(self, x):
        self._x = x

    def d(self):
        del self._x

    def s2(self,x):
        self._x=x+x

    x=property(g,s,d)


c = C()
c.x="a"
print(c.x)

C.x=property(C.g, C.s2)
C.x=C.x.deleter(C.d)
c2 = C()
c2.x="a"
print(c2.x)

输出量

a
aa

Something that works for me is this:

class C:
    def __init__(self):
        self._x=None

    def g(self):
        return self._x

    def s(self, x):
        self._x = x

    def d(self):
        del self._x

    def s2(self,x):
        self._x=x+x

    x=property(g,s,d)


c = C()
c.x="a"
print(c.x)

C.x=property(C.g, C.s2)
C.x=C.x.deleter(C.d)
c2 = C()
c2.x="a"
print(c2.x)

Output

a
aa

回答 20

最近,我遇到了一个类似的问题,即我想出的解决方案使用了它,__getattr__并且__setattr__对于我希望它处理的属性,所有其他信息都传递给了原始文件。

class C(object):
    def __init__(self, properties):
        self.existing = "Still Here"
        self.properties = properties

    def __getattr__(self, name):
        if "properties" in self.__dict__ and name in self.properties:
            return self.properties[name] # Or call a function, etc
        return self.__dict__[name]

    def __setattr__(self, name, value):
        if "properties" in self.__dict__ and name in self.properties:
            self.properties[name] = value
        else:
            self.__dict__[name] = value

if __name__ == "__main__":
    my_properties = {'a':1, 'b':2, 'c':3}
    c = C(my_properties)
    assert c.a == 1
    assert c.existing == "Still Here"
    c.b = 10
    assert c.properties['b'] == 10

I recently ran into a similar problem, the solution that I came up with uses __getattr__ and __setattr__ for the properties that I want it to handle, everything else gets passed on to the originals.

class C(object):
    def __init__(self, properties):
        self.existing = "Still Here"
        self.properties = properties

    def __getattr__(self, name):
        if "properties" in self.__dict__ and name in self.properties:
            return self.properties[name] # Or call a function, etc
        return self.__dict__[name]

    def __setattr__(self, name, value):
        if "properties" in self.__dict__ and name in self.properties:
            self.properties[name] = value
        else:
            self.__dict__[name] = value

if __name__ == "__main__":
    my_properties = {'a':1, 'b':2, 'c':3}
    c = C(my_properties)
    assert c.a == 1
    assert c.existing == "Still Here"
    c.b = 10
    assert c.properties['b'] == 10

回答 21

这是通过编程创建属性对象的简单示例。

#!/usr/bin/python3

class Counter:
    def __init__(self):
        cls = self.__class__
        self._count = 0
        cls.count = self.count_ref()

    def count_get(self):
        print(f'count_get: {self._count}')
        return self._count

    def count_set(self, value):
        self._count = value
        print(f'count_set: {self._count}')

    def count_del(self):
        print(f'count_del: {self._count}')

    def count_ref(self):
        cls = self.__class__
        return property(fget=cls.count_get, fset=cls.count_set, fdel=cls.count_del)

counter = Counter()

counter.count
for i in range(5):
    counter.count = i
del counter.count

'''
output
======
count_get: 0
count_set: 0
count_set: 1
count_set: 2
count_set: 3
count_set: 4
count_del: 4
'''

Here is the simple example to create property object programmatically.

#!/usr/bin/python3

class Counter:
    def __init__(self):
        cls = self.__class__
        self._count = 0
        cls.count = self.count_ref()

    def count_get(self):
        print(f'count_get: {self._count}')
        return self._count

    def count_set(self, value):
        self._count = value
        print(f'count_set: {self._count}')

    def count_del(self):
        print(f'count_del: {self._count}')

    def count_ref(self):
        cls = self.__class__
        return property(fget=cls.count_get, fset=cls.count_set, fdel=cls.count_del)

counter = Counter()

counter.count
for i in range(5):
    counter.count = i
del counter.count

'''
output
======
count_get: 0
count_set: 0
count_set: 1
count_set: 2
count_set: 3
count_set: 4
count_del: 4
'''

回答 22

动态附加属性的唯一方法是使用新属性创建一个新类及其实例。

class Holder: p = property(lambda x: vs[i], self.fn_readonly)
setattr(self, k, Holder().p)

Only way to dynamically attach a property is to create a new class and its instance with your new property.

class Holder: p = property(lambda x: vs[i], self.fn_readonly)
setattr(self, k, Holder().p)

回答 23

提供的很多答案每个属性需要这么多行,即/和/或-由于多个属性需要重复性,我认为这是丑陋或乏味的实现。不能再简化了,或者直到它没有太大用处为止。

简而言之:在完成的作品中,如果我重复两行代码,通常会将其转换为单行辅助函数,以此类推…将数学或奇数参数(例如(start_x,start_y,end_x,end_y)简化为(x,y,w,h)即x,y,x + w,y + h(有时需要min / max或如果w / h为负且实现不喜欢它,我将从x /中减去y和ab w / h等。)。

重写内部getter / setter方法是可行的方法,但是问题是您需要为每个类都这样做,或者将该类作为该基础的父类…这对我不起作用,因为我更希望成为自由选择子代/父代进行继承,子节点等。

我创建了一个解决方案,无需使用Dict数据类型来提供数据即可回答问题,因为我发现输入数据等操作很繁琐,等等。

我的解决方案要求您在类上方添加2行,以为要向其添加属性的类创建基类,然后为每个类添加1行,并且您可以选择添加回调以控制数据,并在数据更改时通知您,限制可以根据值和/或数据类型等进行设置的数据。

您还可以选择使用_object.x,_object.x =值,_object.GetX(),_object.SetX(值),并且它们被等效地处理。

此外,值是分配给类实例的唯一非静态数据,但是实际属性却分配给了类,这意味着您不想重复的事情,不需要重复…可以分配一个默认值,因此每次都有一个不需要的getter,尽管有一个选项可以覆盖默认的默认值,还有另一个选项可以使getter通过覆盖默认的返回值来返回原始存储值(注意:此方法表示仅在分配值时才分配原始值,否则为None-当值重置时,它分配None等。)

辅助函数也有很多-添加的第一个属性将2个左右的辅助函数添加到类中以引用实例值…它们是ResetAccessors(_key,..)varargs重复(所有操作都可以使用第一个命名的args重复进行) )和SetAccessors(_key,_value),还可以选择将更多选项添加到主类中以提高效率-计划的方法是:一种将访问器组合在一起的方式,因此,如果您倾向于每次都重置一些访问器,您可以将它们分配给一个组并重置该组,而不必每次都重复命名的密钥,甚至更多。

实例/原始存储值存储在类中。, 班上。引用访问器类,该类保存该属性的静态vars / values / functions。_类。是属性本身,在设置/获取等过程中通过实例类访问时会调用该属性。

访问器_class .__指向该类,但是由于它是内部的,因此需要在该类中进行分配,这就是为什么我选择使用__Name = AccessorFunc(…)对其进行分配的原因,每个属性一行包含多个可选项要使用的参数(使用键控varargs,是因为它们更容易,更有效地标识和维护)…

如前所述,我还创建了许多函数,其中一些函数使用访问器函数信息,因此不需要调用(因为目前有点不方便-现在您需要使用 _class。.FunctionName(_class_instance ,args)-通过添加运行此马拉松马拉松函数或将访问器添加到对象并使用self(命名为指出它们的方法),我可以使用堆栈/跟踪来获取实例引用以获取值是针对实例的,并保留对自身,AccessorFunc类引用以及函数定义内的其他信息的访问)。

尚未完成,但这是一个了不起的立足点。注意:如果不使用__Name = AccessorFunc(…)创建属性,则即使我在init函数中定义了__键,也无法访问__键。如果这样做,则没有问题。

另外:请注意,名称和键是不同的…名称是“正式的”,在函数名称创建中使用,并且键用于数据存储和访问。即_class.x,其中小写的x是键,名称将是大写的X,因此GetX()是函数,而不是看起来有些奇怪的Getx()。这可以使self.x正常工作并看起来合适,但也可以使GetX()看起来合适。

我有一个示例类,其键/名称相同,但显示不同。为了输出数据而创建了许多辅助函数(注意:并非所有这些都是完整的),因此您可以看到发生了什么。

使用键:x,名称:X的当前功能列表输出为:

这绝不是一个全面的列表-在发布时有一些尚未列入清单…

_instance.SetAccessors( _key, _value [ , _key, _value ] .. )                   Instance Class Helper Function: Allows assigning many keys / values on a single line - useful for initial setup, or to minimize lines.    In short: Calls this.Set<Name>( _value ) for each _key / _value pairing.
_instance.ResetAccessors( _key [ , _key ] .. )                                 Instance Class Helper Function: Allows resetting many key stored values to None on a single line.                                           In short: Calls this.Reset<Name>() for each name provided.


Note: Functions below may list self.Get / Set / Name( _args ) - self is meant as the class instance reference in the cases below - coded as this in AccessorFuncBase Class.

this.GetX( _default_override = None, _ignore_defaults = False )                 GET:            Returns    IF ISSET: STORED_VALUE .. IF IGNORE_DEFAULTS: None  .. IF PROVIDED: DEFAULT_OVERRIDE ELSE: DEFAULT_VALUE       100
this.GetXRaw( )                                                                 RAW:            Returns    STORED_VALUE                                                                                                     100
this.IsXSet( )                                                                  ISSET:          Returns    ( STORED_VALUE != None )                                                                                         True

this.GetXToString( )                                                            GETSTR:         Returns    str( GET )                                                                                                       100
this.GetXLen( _default_override = None, _ignore_defaults = False )              LEN:            Returns    len( GET )                                                                                                       3
this.GetXLenToString( _default_override = None, _ignore_defaults = False )      LENSTR:         Returns    str( len( GET ) )                                                                                                3
this.GetXDefaultValue( )                                                        DEFAULT:        Returns    DEFAULT_VALUE                                                                                                    1111

this.GetXAccessor( )                                                            ACCESSOR:       Returns    ACCESSOR_REF ( self.__<key> )                                                                                    [ AccessorFuncBase ] Key: x : Class ID: 2231452344344 : self ID: 2231448283848        Default: 1111       Allowed Types: {"<class 'int'>": "<class 'type'>", "<class 'float'>": "<class 'type'>"}     Allowed Values: None
this.GetXAllowedTypes( )                                                        ALLOWED_TYPES:  Returns    Allowed Data-Types                                                                                               {"<class 'int'>": "<class 'type'>", "<class 'float'>": "<class 'type'>"}
this.GetXAllowedValues( )                                                       ALLOWED_VALUES: Returns    Allowed Values                                                                                                   None

this.GetXHelpers( )                                                             HELPERS:        Returns    Helper Functions String List - ie what you're reading now...                                                     THESE ROWS OF TEXT
this.GetXKeyOutput( )                                                           Returns information about this Name / Key                                                                                                   ROWS OF TEXT
this.GetXGetterOutput( )                                                        Returns information about this Name / Key                                                                                                   ROWS OF TEXT

this.SetX( _value )                                                             SET:            STORED_VALUE Setter - ie Redirect to __<Key>.Set                                                                            N / A
this.ResetX( )                                                                  RESET:          Resets STORED_VALUE to None                                                                                                 N / A

this.HasXGetterPrefix( )                                                        Returns Whether or Not this key has a Getter Prefix...                                                                                      True
this.GetXGetterPrefix( )                                                        Returns Getter Prefix...                                                                                                                    Get

this.GetXName( )                                                                Returns Accessor Name - Typically Formal / Title-Case                                                                                       X
this.GetXKey( )                                                                 Returns Accessor Property Key - Typically Lower-Case                                                                                        x
this.GetXAccessorKey( )                                                         Returns Accessor Key - This is to access internal functions, and static data...                                                             __x
this.GetXDataKey( )                                                             Returns Accessor Data-Storage Key - This is the location where the class instance value is stored..                                         _x

正在输出的一些数据是:

这是使用Demo类创建的全新类,除了名称_foo(我使用的变量名称)之外,没有分配任何数据(因此可以输出)。

_foo         --- MyClass: ---- id( this.__class__ ): 2231452349064 :::: id( this ): 2231448475016

    Key       Getter Value        | Raw Key   Raw / Stored Value       | Get Default Value             Default Value            | Get Allowed Types             Allowed Types                                                              | Get Allowed Values            Allowed Values                                                                                                                                                                                                                   |

    Name:     _foo                | _Name:    _foo                     | __Name.DefaultValue( ):       AccessorFuncDemoClass    | __Name.GetAllowedTypes( )     <class 'str'>                                                              | __Name.GetAllowedValues( )    Saved Value Restrictions Levied by Data-Type                                                                                                                                                                                     |
    x:        1111                | _x:       None                     | __x.DefaultValue( ):          1111                     | __x.GetAllowedTypes( )        (<class 'int'>, <class 'float'>)                                           | __x.GetAllowedValues( )       Saved Value Restrictions Levied by Data-Type                                                                                                                                                                                     |
    y:        2222                | _y:       None                     | __y.DefaultValue( ):          2222                     | __y.GetAllowedTypes( )        (<class 'int'>, <class 'float'>)                                           | __y.GetAllowedValues( )       Saved Value Restrictions Levied by Data-Type                                                                                                                                                                                     |
    z:        3333                | _z:       None                     | __z.DefaultValue( ):          3333                     | __z.GetAllowedTypes( )        (<class 'int'>, <class 'float'>)                                           | __z.GetAllowedValues( )       Saved Value Restrictions Levied by Data-Type                                                                                                                                                                                     |
    Blah:     <class 'int'>       | _Blah:    None                     | __Blah.DefaultValue( ):       <class 'int'>            | __Blah.GetAllowedTypes( )     <class 'str'>                                                              | __Blah.GetAllowedValues( )    Saved Value Restrictions Levied by Data-Type                                                                                                                                                                                     |
    Width:    1                   | _Width:   None                     | __Width.DefaultValue( ):      1                        | __Width.GetAllowedTypes( )    (<class 'int'>, <class 'bool'>)                                            | __Width.GetAllowedValues( )   Saved Value Restrictions Levied by Data-Type                                                                                                                                                                                     |
    Height:   0                   | _Height:  None                     | __Height.DefaultValue( ):     0                        | __Height.GetAllowedTypes( )   <class 'int'>                                                              | __Height.GetAllowedValues( )  (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)                                                                                                                                                                                                   |
    Depth:    2                   | _Depth:   None                     | __Depth.DefaultValue( ):      2                        | __Depth.GetAllowedTypes( )    Saved Value Restricted to Authorized Values ONLY                           | __Depth.GetAllowedValues( )   (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)                                                                                                                                                                                                   |


this.IsNameSet( ):    True      this.GetName( ):     _foo                     this.GetNameRaw( ):    _foo                     this.GetNameDefaultValue( ):    AccessorFuncDemoClass    this.GetNameLen( ):    4    this.HasNameGetterPrefix( ):    <class 'str'>                                this.GetNameGetterPrefix( ):    None
this.IsXSet( ):       False     this.GetX( ):        1111                     this.GetXRaw( ):       None                     this.GetXDefaultValue( ):       1111                     this.GetXLen( ):       4    this.HasXGetterPrefix( ):       (<class 'int'>, <class 'float'>)             this.GetXGetterPrefix( ):       None
this.IsYSet( ):       False     this.GetY( ):        2222                     this.GetYRaw( ):       None                     this.GetYDefaultValue( ):       2222                     this.GetYLen( ):       4    this.HasYGetterPrefix( ):       (<class 'int'>, <class 'float'>)             this.GetYGetterPrefix( ):       None
this.IsZSet( ):       False     this.GetZ( ):        3333                     this.GetZRaw( ):       None                     this.GetZDefaultValue( ):       3333                     this.GetZLen( ):       4    this.HasZGetterPrefix( ):       (<class 'int'>, <class 'float'>)             this.GetZGetterPrefix( ):       None
this.IsBlahSet( ):    False     this.GetBlah( ):     <class 'int'>            this.GetBlahRaw( ):    None                     this.GetBlahDefaultValue( ):    <class 'int'>            this.GetBlahLen( ):    13   this.HasBlahGetterPrefix( ):    <class 'str'>                                this.GetBlahGetterPrefix( ):    None
this.IsWidthSet( ):   False     this.GetWidth( ):    1                        this.GetWidthRaw( ):   None                     this.GetWidthDefaultValue( ):   1                        this.GetWidthLen( ):   1    this.HasWidthGetterPrefix( ):   (<class 'int'>, <class 'bool'>)              this.GetWidthGetterPrefix( ):   None
this.IsDepthSet( ):   False     this.GetDepth( ):    2                        this.GetDepthRaw( ):   None                     this.GetDepthDefaultValue( ):   2                        this.GetDepthLen( ):   1    this.HasDepthGetterPrefix( ):   None                                         this.GetDepthGetterPrefix( ):   (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
this.IsHeightSet( ):  False     this.GetHeight( ):   0                        this.GetHeightRaw( ):  None                     this.GetHeightDefaultValue( ):  0                        this.GetHeightLen( ):  1    this.HasHeightGetterPrefix( ):  <class 'int'>                                this.GetHeightGetterPrefix( ):  (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)

这是在为所有_foo属性(名称除外)按相同顺序分配以下值之后:’string’,1.0,True,9,10,False

this.IsNameSet( ):    True      this.GetName( ):     _foo                     this.GetNameRaw( ):    _foo                     this.GetNameDefaultValue( ):    AccessorFuncDemoClass    this.GetNameLen( ):    4    this.HasNameGetterPrefix( ):    <class 'str'>                                this.GetNameGetterPrefix( ):    None
this.IsXSet( ):       True      this.GetX( ):        10                       this.GetXRaw( ):       10                       this.GetXDefaultValue( ):       1111                     this.GetXLen( ):       2    this.HasXGetterPrefix( ):       (<class 'int'>, <class 'float'>)             this.GetXGetterPrefix( ):       None
this.IsYSet( ):       True      this.GetY( ):        10                       this.GetYRaw( ):       10                       this.GetYDefaultValue( ):       2222                     this.GetYLen( ):       2    this.HasYGetterPrefix( ):       (<class 'int'>, <class 'float'>)             this.GetYGetterPrefix( ):       None
this.IsZSet( ):       True      this.GetZ( ):        10                       this.GetZRaw( ):       10                       this.GetZDefaultValue( ):       3333                     this.GetZLen( ):       2    this.HasZGetterPrefix( ):       (<class 'int'>, <class 'float'>)             this.GetZGetterPrefix( ):       None
this.IsBlahSet( ):    True      this.GetBlah( ):     string Blah              this.GetBlahRaw( ):    string Blah              this.GetBlahDefaultValue( ):    <class 'int'>            this.GetBlahLen( ):    11   this.HasBlahGetterPrefix( ):    <class 'str'>                                this.GetBlahGetterPrefix( ):    None
this.IsWidthSet( ):   True      this.GetWidth( ):    False                    this.GetWidthRaw( ):   False                    this.GetWidthDefaultValue( ):   1                        this.GetWidthLen( ):   5    this.HasWidthGetterPrefix( ):   (<class 'int'>, <class 'bool'>)              this.GetWidthGetterPrefix( ):   None
this.IsDepthSet( ):   True      this.GetDepth( ):    9                        this.GetDepthRaw( ):   9                        this.GetDepthDefaultValue( ):   2                        this.GetDepthLen( ):   1    this.HasDepthGetterPrefix( ):   None                                         this.GetDepthGetterPrefix( ):   (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
this.IsHeightSet( ):  True      this.GetHeight( ):   9                        this.GetHeightRaw( ):  9                        this.GetHeightDefaultValue( ):  0                        this.GetHeightLen( ):  1    this.HasHeightGetterPrefix( ):  <class 'int'>                                this.GetHeightGetterPrefix( ):  (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)

_foo         --- MyClass: ---- id( this.__class__ ): 2231452349064 :::: id( this ): 2231448475016

    Key       Getter Value        | Raw Key   Raw / Stored Value       | Get Default Value             Default Value            | Get Allowed Types             Allowed Types                                                              | Get Allowed Values            Allowed Values                                                                                                                                                                                                                   |

    Name:     _foo                | _Name:    _foo                     | __Name.DefaultValue( ):       AccessorFuncDemoClass    | __Name.GetAllowedTypes( )     <class 'str'>                                                              | __Name.GetAllowedValues( )    Saved Value Restrictions Levied by Data-Type                                                                                                                                                                                     |
    x:        10                  | _x:       10                       | __x.DefaultValue( ):          1111                     | __x.GetAllowedTypes( )        (<class 'int'>, <class 'float'>)                                           | __x.GetAllowedValues( )       Saved Value Restrictions Levied by Data-Type                                                                                                                                                                                     |
    y:        10                  | _y:       10                       | __y.DefaultValue( ):          2222                     | __y.GetAllowedTypes( )        (<class 'int'>, <class 'float'>)                                           | __y.GetAllowedValues( )       Saved Value Restrictions Levied by Data-Type                                                                                                                                                                                     |
    z:        10                  | _z:       10                       | __z.DefaultValue( ):          3333                     | __z.GetAllowedTypes( )        (<class 'int'>, <class 'float'>)                                           | __z.GetAllowedValues( )       Saved Value Restrictions Levied by Data-Type                                                                                                                                                                                     |
    Blah:     string Blah         | _Blah:    string Blah              | __Blah.DefaultValue( ):       <class 'int'>            | __Blah.GetAllowedTypes( )     <class 'str'>                                                              | __Blah.GetAllowedValues( )    Saved Value Restrictions Levied by Data-Type                                                                                                                                                                                     |
    Width:    False               | _Width:   False                    | __Width.DefaultValue( ):      1                        | __Width.GetAllowedTypes( )    (<class 'int'>, <class 'bool'>)                                            | __Width.GetAllowedValues( )   Saved Value Restrictions Levied by Data-Type                                                                                                                                                                                     |
    Height:   9                   | _Height:  9                        | __Height.DefaultValue( ):     0                        | __Height.GetAllowedTypes( )   <class 'int'>                                                              | __Height.GetAllowedValues( )  (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)                                                                                                                                                                                                   |
    Depth:    9                   | _Depth:   9                        | __Depth.DefaultValue( ):      2                        | __Depth.GetAllowedTypes( )    Saved Value Restricted to Authorized Values ONLY                           | __Depth.GetAllowedValues( )   (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)                                                                                                                                                                                                   |

请注意,由于数据类型或值的限制,未分配某些数据-这是设计使然。设置器禁止分配错误的数据类型或值,甚至禁止将其分配为默认值(除非您覆盖默认值保护行为)

该代码尚未发布在这里,因为在示例和说明之后我没有足够的空间…也是因为它将更改。

请注意:在发布时,文件杂乱无章-这将改变。但是,如果您在Sublime Text中运行它并进行编译,或者从Python运行它,它将编译并吐出大量信息-AccessorDB部分未完成(将用于更新Print Getters和GetKeyOutput帮助器)函数以及被更改为Instance函数,可能放在一个函数中并重命名-查找它。

下一步:运行它并不需要所有内容-底部的很多注释内容是用于调试的更多信息-下载时可能不存在。如果是这样,您应该可以取消注释并重新编译以获得更多信息。

我正在寻找一种解决方案,需要MyClassBase:通过,MyClass(MyClassBase):…-如果您知道解决方案,请发布它。

该类中唯一需要的是__行-strinit一样都是用于调试的-可以将它们从Demo类中删除,但是您需要注释掉或删除下面的一些行(_foo / 2/3 )..

顶部的String,Dict和Util类是我的Python库的一部分-它们不完整。我从库中复制了一些我需要的东西,然后创建了一些新东西。完整的代码将链接到完整的库,并将包括完整的库以及提供更新的调用和删除代码(实际上,剩下的唯一代码将是Demo类和print语句-AccessorFunc系统将移至库)。 ..

文件部分:

##
## MyClass Test AccessorFunc Implementation for Dynamic 1-line Parameters
##
class AccessorFuncDemoClassBase( ):
    pass
class AccessorFuncDemoClass( AccessorFuncDemoClassBase ):
    __Name      = AccessorFuncBase( parent = AccessorFuncDemoClassBase, name = 'Name',      default = 'AccessorFuncDemoClass',  allowed_types = ( TYPE_STRING ),                    allowed_values = VALUE_ANY,                 documentation = 'Name Docs',        getter_prefix = 'Get',  key = 'Name',       allow_erroneous_default = False,    options = { } )
    __x         = AccessorFuncBase( parent = AccessorFuncDemoClassBase, name = 'X',         default = 1111,                     allowed_types = ( TYPE_INTEGER, TYPE_FLOAT ),       allowed_values = VALUE_ANY,                 documentation = 'X Docs',           getter_prefix = 'Get',  key = 'x',          allow_erroneous_default = False,    options = { } )
    __Height    = AccessorFuncBase( parent = AccessorFuncDemoClassBase, name = 'Height',    default = 0,                        allowed_types = TYPE_INTEGER,                       allowed_values = VALUE_SINGLE_DIGITS,       documentation = 'Height Docs',      getter_prefix = 'Get',  key = 'Height',     allow_erroneous_default = False,    options = { } )

这种美使得通过AccessorFuncs /回调/数据类型/值强制等动态添加属性来创建新类变得异常容易。

目前,链接位于(此链接应反映对文档的更改。):https : //www.dropbox.com/s/6gzi44i7dh58v61/dynamic_properties_accessorfuncs_and_more.py?dl=0

另外:如果您不使用Sublime Text,我建议在Notepad ++,Atom,Visual Code和其他语言上使用它,因为适当的线程实现使其使用起来非常快得多…我也在研究类似IDE的代码映射系统-请看一下:https : //bitbucket.org/Acecool/acecoolcodemappingsystem/src/master/(首先在软件包管理器中添加Repo,然后再安装插件-在1.0.0版本准备就绪时,我将添加它到主插件列表…)

我希望这个解决方案能对您有所帮助……并且一如既往:

仅仅因为它有效,就不能使它正确-Josh’Acecool’Moser

A lot of the supplied answers require so many lines per property, ie / and / or – what I’d consider an ugly or tedious implementation because of repetitiveness required for multiple properties, etc. I prefer keeping boiling things down / simplifying them until they can’t be simplified anymore or until it doesn’t serve much purpose to do so.

In short: in completed works, if I repeat 2 lines of code, I typically convert it into a single line helper function, and so on… I simplify math or odd arguments such as ( start_x, start_y, end_x, end_y ) to ( x, y, w, h ) ie x, y, x + w, y + h ( sometimes requiring min / max or if w / h are negative and the implementation doesn’t like it, I’ll subtract from x / y and abs w / h. etc.. ).

Overriding the internal getters / setters is an ok way to go, but the problem is you need to do that for every class, or parent the class to that base… This doesn’t work for me as I’d prefer to be free to choose the children / parents for inheritance, child nodes, etc.

I have created a solution which answers the question without using a Dict data-type to supply the data as I find that to be tedious to enter the data, etc…

My solution requires you to add 2 extra lines above your class to create a base class for the class you want to add the properties to, then 1 line per and you have the option to add callbacks to control the data, inform you when data changes, restrict the data which can be set based on value and / or data-type, and much more.

You also have the option to use _object.x, _object.x = value, _object.GetX( ), _object.SetX( value ) and they are handled equivalently.

Additionally, the values are the only non-static data which are assigned to the class instance, but the actual property is assigned to the class meaning the things you don’t want to repeat, don’t need to be repeated… You can assign a default value so the getter doesn’t need it each time, although there is an option to override the default default value, and there is another option so the getter returns the raw stored value by overriding default returns ( note: this method means the raw value is only assigned when a value is assigned, otherwise it is None – when the value is Reset, then it assigns None, etc.. )

There are many helper functions too – the first property which gets added adds 2 or so helpers to the class for referencing the instance values… They are ResetAccessors( _key, .. ) varargs repeated ( all can be repeated using the first named args ) and SetAccessors( _key, _value ) with the option of more being added to the main class to aide in efficiency – the ones planned are: a way to group accessors together, so if you tend to reset a few at a time, every time, you can assign them to a group and reset the group instead of repeating the named keys each time, and more.

The instance / raw stored value is stored at class., the __class. references the Accessor Class which holds static vars / values / functions for the property. _class. is the property itself which is called when accessed via the instance class during setting / getting, etc.

The Accessor _class.__ points to the class, but because it is internal it needs to be assigned in the class which is why I opted to use __Name = AccessorFunc( … ) to assign it, a single line per property with many optional arguments to use ( using keyed varargs because they’re easier and more efficient to identify and maintain )…

I also create a lot of functions, as mentioned, some of which use accessor function information so it doesn’t need to be called ( as it is a bit inconvenient at the moment – right now you need to use _class..FunctionName( _class_instance, args ) – I got around using the stack / trace to grab the instance reference to grab the value by adding the functions which either run this bit marathon, or by adding the accessors to the object and using self ( named this to point out they’re for the instance and to retain access to self, the AccessorFunc class reference, and other information from within the function definitions ).

It isn’t quite done, but it is a fantastic foot-hold. Note: If you do not use __Name = AccessorFunc( … ) to create the properties, you won’t have access to the __ key even though I define it within the init function. If you do, then there are no issues.

Also: Note that Name and Key are different… Name is ‘formal’, used in Function Name Creation, and the key is for data storage and access. ie _class.x where lowercase x is key, the name would be uppercase X so that GetX( ) is the function instead of Getx( ) which looks a little odd. this allows self.x to work and look appropriate, but also allow GetX( ) and look appropriate.

I have an example class set up with key / name identical, and different to show. a lot of helper functions created in order to output the data ( Note: Not all of this is complete ) so you can see what is going on.

The current list of functions using key: x, name: X outputs as:

This is by no means a comprehensive list – there are a few which haven’t made it on this at the time of posting…

_instance.SetAccessors( _key, _value [ , _key, _value ] .. )                   Instance Class Helper Function: Allows assigning many keys / values on a single line - useful for initial setup, or to minimize lines.    In short: Calls this.Set<Name>( _value ) for each _key / _value pairing.
_instance.ResetAccessors( _key [ , _key ] .. )                                 Instance Class Helper Function: Allows resetting many key stored values to None on a single line.                                           In short: Calls this.Reset<Name>() for each name provided.


Note: Functions below may list self.Get / Set / Name( _args ) - self is meant as the class instance reference in the cases below - coded as this in AccessorFuncBase Class.

this.GetX( _default_override = None, _ignore_defaults = False )                 GET:            Returns    IF ISSET: STORED_VALUE .. IF IGNORE_DEFAULTS: None  .. IF PROVIDED: DEFAULT_OVERRIDE ELSE: DEFAULT_VALUE       100
this.GetXRaw( )                                                                 RAW:            Returns    STORED_VALUE                                                                                                     100
this.IsXSet( )                                                                  ISSET:          Returns    ( STORED_VALUE != None )                                                                                         True

this.GetXToString( )                                                            GETSTR:         Returns    str( GET )                                                                                                       100
this.GetXLen( _default_override = None, _ignore_defaults = False )              LEN:            Returns    len( GET )                                                                                                       3
this.GetXLenToString( _default_override = None, _ignore_defaults = False )      LENSTR:         Returns    str( len( GET ) )                                                                                                3
this.GetXDefaultValue( )                                                        DEFAULT:        Returns    DEFAULT_VALUE                                                                                                    1111

this.GetXAccessor( )                                                            ACCESSOR:       Returns    ACCESSOR_REF ( self.__<key> )                                                                                    [ AccessorFuncBase ] Key: x : Class ID: 2231452344344 : self ID: 2231448283848        Default: 1111       Allowed Types: {"<class 'int'>": "<class 'type'>", "<class 'float'>": "<class 'type'>"}     Allowed Values: None
this.GetXAllowedTypes( )                                                        ALLOWED_TYPES:  Returns    Allowed Data-Types                                                                                               {"<class 'int'>": "<class 'type'>", "<class 'float'>": "<class 'type'>"}
this.GetXAllowedValues( )                                                       ALLOWED_VALUES: Returns    Allowed Values                                                                                                   None

this.GetXHelpers( )                                                             HELPERS:        Returns    Helper Functions String List - ie what you're reading now...                                                     THESE ROWS OF TEXT
this.GetXKeyOutput( )                                                           Returns information about this Name / Key                                                                                                   ROWS OF TEXT
this.GetXGetterOutput( )                                                        Returns information about this Name / Key                                                                                                   ROWS OF TEXT

this.SetX( _value )                                                             SET:            STORED_VALUE Setter - ie Redirect to __<Key>.Set                                                                            N / A
this.ResetX( )                                                                  RESET:          Resets STORED_VALUE to None                                                                                                 N / A

this.HasXGetterPrefix( )                                                        Returns Whether or Not this key has a Getter Prefix...                                                                                      True
this.GetXGetterPrefix( )                                                        Returns Getter Prefix...                                                                                                                    Get

this.GetXName( )                                                                Returns Accessor Name - Typically Formal / Title-Case                                                                                       X
this.GetXKey( )                                                                 Returns Accessor Property Key - Typically Lower-Case                                                                                        x
this.GetXAccessorKey( )                                                         Returns Accessor Key - This is to access internal functions, and static data...                                                             __x
this.GetXDataKey( )                                                             Returns Accessor Data-Storage Key - This is the location where the class instance value is stored..                                         _x

Some of the data being output is:

This is for a brand new class created using the Demo class without any data assigned other than the name ( so it can be output ) which is _foo, the variable name I used…

_foo         --- MyClass: ---- id( this.__class__ ): 2231452349064 :::: id( this ): 2231448475016

    Key       Getter Value        | Raw Key   Raw / Stored Value       | Get Default Value             Default Value            | Get Allowed Types             Allowed Types                                                              | Get Allowed Values            Allowed Values                                                                                                                                                                                                                   |

    Name:     _foo                | _Name:    _foo                     | __Name.DefaultValue( ):       AccessorFuncDemoClass    | __Name.GetAllowedTypes( )     <class 'str'>                                                              | __Name.GetAllowedValues( )    Saved Value Restrictions Levied by Data-Type                                                                                                                                                                                     |
    x:        1111                | _x:       None                     | __x.DefaultValue( ):          1111                     | __x.GetAllowedTypes( )        (<class 'int'>, <class 'float'>)                                           | __x.GetAllowedValues( )       Saved Value Restrictions Levied by Data-Type                                                                                                                                                                                     |
    y:        2222                | _y:       None                     | __y.DefaultValue( ):          2222                     | __y.GetAllowedTypes( )        (<class 'int'>, <class 'float'>)                                           | __y.GetAllowedValues( )       Saved Value Restrictions Levied by Data-Type                                                                                                                                                                                     |
    z:        3333                | _z:       None                     | __z.DefaultValue( ):          3333                     | __z.GetAllowedTypes( )        (<class 'int'>, <class 'float'>)                                           | __z.GetAllowedValues( )       Saved Value Restrictions Levied by Data-Type                                                                                                                                                                                     |
    Blah:     <class 'int'>       | _Blah:    None                     | __Blah.DefaultValue( ):       <class 'int'>            | __Blah.GetAllowedTypes( )     <class 'str'>                                                              | __Blah.GetAllowedValues( )    Saved Value Restrictions Levied by Data-Type                                                                                                                                                                                     |
    Width:    1                   | _Width:   None                     | __Width.DefaultValue( ):      1                        | __Width.GetAllowedTypes( )    (<class 'int'>, <class 'bool'>)                                            | __Width.GetAllowedValues( )   Saved Value Restrictions Levied by Data-Type                                                                                                                                                                                     |
    Height:   0                   | _Height:  None                     | __Height.DefaultValue( ):     0                        | __Height.GetAllowedTypes( )   <class 'int'>                                                              | __Height.GetAllowedValues( )  (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)                                                                                                                                                                                                   |
    Depth:    2                   | _Depth:   None                     | __Depth.DefaultValue( ):      2                        | __Depth.GetAllowedTypes( )    Saved Value Restricted to Authorized Values ONLY                           | __Depth.GetAllowedValues( )   (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)                                                                                                                                                                                                   |


this.IsNameSet( ):    True      this.GetName( ):     _foo                     this.GetNameRaw( ):    _foo                     this.GetNameDefaultValue( ):    AccessorFuncDemoClass    this.GetNameLen( ):    4    this.HasNameGetterPrefix( ):    <class 'str'>                                this.GetNameGetterPrefix( ):    None
this.IsXSet( ):       False     this.GetX( ):        1111                     this.GetXRaw( ):       None                     this.GetXDefaultValue( ):       1111                     this.GetXLen( ):       4    this.HasXGetterPrefix( ):       (<class 'int'>, <class 'float'>)             this.GetXGetterPrefix( ):       None
this.IsYSet( ):       False     this.GetY( ):        2222                     this.GetYRaw( ):       None                     this.GetYDefaultValue( ):       2222                     this.GetYLen( ):       4    this.HasYGetterPrefix( ):       (<class 'int'>, <class 'float'>)             this.GetYGetterPrefix( ):       None
this.IsZSet( ):       False     this.GetZ( ):        3333                     this.GetZRaw( ):       None                     this.GetZDefaultValue( ):       3333                     this.GetZLen( ):       4    this.HasZGetterPrefix( ):       (<class 'int'>, <class 'float'>)             this.GetZGetterPrefix( ):       None
this.IsBlahSet( ):    False     this.GetBlah( ):     <class 'int'>            this.GetBlahRaw( ):    None                     this.GetBlahDefaultValue( ):    <class 'int'>            this.GetBlahLen( ):    13   this.HasBlahGetterPrefix( ):    <class 'str'>                                this.GetBlahGetterPrefix( ):    None
this.IsWidthSet( ):   False     this.GetWidth( ):    1                        this.GetWidthRaw( ):   None                     this.GetWidthDefaultValue( ):   1                        this.GetWidthLen( ):   1    this.HasWidthGetterPrefix( ):   (<class 'int'>, <class 'bool'>)              this.GetWidthGetterPrefix( ):   None
this.IsDepthSet( ):   False     this.GetDepth( ):    2                        this.GetDepthRaw( ):   None                     this.GetDepthDefaultValue( ):   2                        this.GetDepthLen( ):   1    this.HasDepthGetterPrefix( ):   None                                         this.GetDepthGetterPrefix( ):   (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
this.IsHeightSet( ):  False     this.GetHeight( ):   0                        this.GetHeightRaw( ):  None                     this.GetHeightDefaultValue( ):  0                        this.GetHeightLen( ):  1    this.HasHeightGetterPrefix( ):  <class 'int'>                                this.GetHeightGetterPrefix( ):  (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)

And this is after assigning all of _foo properties ( except the name ) the following values in the same order: ‘string ‘, 1.0, True, 9, 10, False

this.IsNameSet( ):    True      this.GetName( ):     _foo                     this.GetNameRaw( ):    _foo                     this.GetNameDefaultValue( ):    AccessorFuncDemoClass    this.GetNameLen( ):    4    this.HasNameGetterPrefix( ):    <class 'str'>                                this.GetNameGetterPrefix( ):    None
this.IsXSet( ):       True      this.GetX( ):        10                       this.GetXRaw( ):       10                       this.GetXDefaultValue( ):       1111                     this.GetXLen( ):       2    this.HasXGetterPrefix( ):       (<class 'int'>, <class 'float'>)             this.GetXGetterPrefix( ):       None
this.IsYSet( ):       True      this.GetY( ):        10                       this.GetYRaw( ):       10                       this.GetYDefaultValue( ):       2222                     this.GetYLen( ):       2    this.HasYGetterPrefix( ):       (<class 'int'>, <class 'float'>)             this.GetYGetterPrefix( ):       None
this.IsZSet( ):       True      this.GetZ( ):        10                       this.GetZRaw( ):       10                       this.GetZDefaultValue( ):       3333                     this.GetZLen( ):       2    this.HasZGetterPrefix( ):       (<class 'int'>, <class 'float'>)             this.GetZGetterPrefix( ):       None
this.IsBlahSet( ):    True      this.GetBlah( ):     string Blah              this.GetBlahRaw( ):    string Blah              this.GetBlahDefaultValue( ):    <class 'int'>            this.GetBlahLen( ):    11   this.HasBlahGetterPrefix( ):    <class 'str'>                                this.GetBlahGetterPrefix( ):    None
this.IsWidthSet( ):   True      this.GetWidth( ):    False                    this.GetWidthRaw( ):   False                    this.GetWidthDefaultValue( ):   1                        this.GetWidthLen( ):   5    this.HasWidthGetterPrefix( ):   (<class 'int'>, <class 'bool'>)              this.GetWidthGetterPrefix( ):   None
this.IsDepthSet( ):   True      this.GetDepth( ):    9                        this.GetDepthRaw( ):   9                        this.GetDepthDefaultValue( ):   2                        this.GetDepthLen( ):   1    this.HasDepthGetterPrefix( ):   None                                         this.GetDepthGetterPrefix( ):   (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
this.IsHeightSet( ):  True      this.GetHeight( ):   9                        this.GetHeightRaw( ):  9                        this.GetHeightDefaultValue( ):  0                        this.GetHeightLen( ):  1    this.HasHeightGetterPrefix( ):  <class 'int'>                                this.GetHeightGetterPrefix( ):  (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)

_foo         --- MyClass: ---- id( this.__class__ ): 2231452349064 :::: id( this ): 2231448475016

    Key       Getter Value        | Raw Key   Raw / Stored Value       | Get Default Value             Default Value            | Get Allowed Types             Allowed Types                                                              | Get Allowed Values            Allowed Values                                                                                                                                                                                                                   |

    Name:     _foo                | _Name:    _foo                     | __Name.DefaultValue( ):       AccessorFuncDemoClass    | __Name.GetAllowedTypes( )     <class 'str'>                                                              | __Name.GetAllowedValues( )    Saved Value Restrictions Levied by Data-Type                                                                                                                                                                                     |
    x:        10                  | _x:       10                       | __x.DefaultValue( ):          1111                     | __x.GetAllowedTypes( )        (<class 'int'>, <class 'float'>)                                           | __x.GetAllowedValues( )       Saved Value Restrictions Levied by Data-Type                                                                                                                                                                                     |
    y:        10                  | _y:       10                       | __y.DefaultValue( ):          2222                     | __y.GetAllowedTypes( )        (<class 'int'>, <class 'float'>)                                           | __y.GetAllowedValues( )       Saved Value Restrictions Levied by Data-Type                                                                                                                                                                                     |
    z:        10                  | _z:       10                       | __z.DefaultValue( ):          3333                     | __z.GetAllowedTypes( )        (<class 'int'>, <class 'float'>)                                           | __z.GetAllowedValues( )       Saved Value Restrictions Levied by Data-Type                                                                                                                                                                                     |
    Blah:     string Blah         | _Blah:    string Blah              | __Blah.DefaultValue( ):       <class 'int'>            | __Blah.GetAllowedTypes( )     <class 'str'>                                                              | __Blah.GetAllowedValues( )    Saved Value Restrictions Levied by Data-Type                                                                                                                                                                                     |
    Width:    False               | _Width:   False                    | __Width.DefaultValue( ):      1                        | __Width.GetAllowedTypes( )    (<class 'int'>, <class 'bool'>)                                            | __Width.GetAllowedValues( )   Saved Value Restrictions Levied by Data-Type                                                                                                                                                                                     |
    Height:   9                   | _Height:  9                        | __Height.DefaultValue( ):     0                        | __Height.GetAllowedTypes( )   <class 'int'>                                                              | __Height.GetAllowedValues( )  (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)                                                                                                                                                                                                   |
    Depth:    9                   | _Depth:   9                        | __Depth.DefaultValue( ):      2                        | __Depth.GetAllowedTypes( )    Saved Value Restricted to Authorized Values ONLY                           | __Depth.GetAllowedValues( )   (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)                                                                                                                                                                                                   |

Note that because of restricted data-types or value restrictions, some data wasn’t assigned – this is by design. The setter prohibits bad data-types or values from being assigned, even from being assigned as a default value ( unless you override the default value protection behavior )

The code hasn’t been posted here because I didn’t have room after the examples and explanations… Also because it will change.

Please Note: at the time of this posting, the file is messy – this will change. But, if you run it in Sublime Text and compile it, or run it from Python, it will compile and spit out a ton of information – the AccessorDB portion isn’t done ( which will be used to update the Print Getters and GetKeyOutput helper functions along with being changed to an Instance function, probably put into a single function and renamed – look for it.. )

Next: Not everything is required for it to run – a lot of the commented stuff at the bottom is for more information used for debugging – it may not be there when you download it. If it is, you should be able to uncomment and recompile to get more information.

I am looking for a work-around to needing MyClassBase: pass, MyClass( MyClassBase ): … – if you know of a solution – post it.

The only thing necessary in the class are the __ lines – the str is for debugging as is the init – they can be removed from the Demo Class but you will need to comment out or remove some of the lines below ( _foo / 2 / 3 )..

The String, Dict, and Util classes at the top are a part of my Python library – they are not complete. I copied over a few things I needed from the library, and I created a few new ones. The full code will link to the complete library and will include it along with providing updated calls and removing the code ( actually, the only code left will be the Demo Class and the print statements – the AccessorFunc system will be moved to the library )…

Part of file:

##
## MyClass Test AccessorFunc Implementation for Dynamic 1-line Parameters
##
class AccessorFuncDemoClassBase( ):
    pass
class AccessorFuncDemoClass( AccessorFuncDemoClassBase ):
    __Name      = AccessorFuncBase( parent = AccessorFuncDemoClassBase, name = 'Name',      default = 'AccessorFuncDemoClass',  allowed_types = ( TYPE_STRING ),                    allowed_values = VALUE_ANY,                 documentation = 'Name Docs',        getter_prefix = 'Get',  key = 'Name',       allow_erroneous_default = False,    options = { } )
    __x         = AccessorFuncBase( parent = AccessorFuncDemoClassBase, name = 'X',         default = 1111,                     allowed_types = ( TYPE_INTEGER, TYPE_FLOAT ),       allowed_values = VALUE_ANY,                 documentation = 'X Docs',           getter_prefix = 'Get',  key = 'x',          allow_erroneous_default = False,    options = { } )
    __Height    = AccessorFuncBase( parent = AccessorFuncDemoClassBase, name = 'Height',    default = 0,                        allowed_types = TYPE_INTEGER,                       allowed_values = VALUE_SINGLE_DIGITS,       documentation = 'Height Docs',      getter_prefix = 'Get',  key = 'Height',     allow_erroneous_default = False,    options = { } )

This beauty makes it incredibly easy to create new classes with dynamically added properties with AccessorFuncs / callbacks / data-type / value enforcement, etc.

For now, the link is at ( This link should reflect changes to the document. ): https://www.dropbox.com/s/6gzi44i7dh58v61/dynamic_properties_accessorfuncs_and_more.py?dl=0

Also: If you don’t use Sublime Text, I recommend it over Notepad++, Atom, Visual Code, and others because of proper threading implementations making it much, much faster to use… I am also working on an IDE-like code mapping system for it – take a look at: https://bitbucket.org/Acecool/acecoolcodemappingsystem/src/master/ ( Add Repo in Package Manager first, then Install Plugin – when version 1.0.0 is ready, I’ll add it to the main plugin list… )

I hope this solution helps… and, as always:

Just because it works, doesn’t make it right – Josh ‘Acecool’ Moser


@property装饰器如何工作?

问题:@property装饰器如何工作?

我想了解内置函数的property工作原理。令我感到困惑的是,property它还可以用作装饰器,但是仅当用作内置函数时才接受参数,而不能用作装饰器。

这个例子来自文档

class C(object):
    def __init__(self):
        self._x = None

    def getx(self):
        return self._x
    def setx(self, value):
        self._x = value
    def delx(self):
        del self._x
    x = property(getx, setx, delx, "I'm the 'x' property.")

property的论点是getxsetxdelx和文档字符串。

在下面的代码中property用作装饰器。它的对象是x函数,但是在上面的代码中,参数中没有对象函数的位置。

class C(object):
    def __init__(self):
        self._x = None

    @property
    def x(self):
        """I'm the 'x' property."""
        return self._x

    @x.setter
    def x(self, value):
        self._x = value

    @x.deleter
    def x(self):
        del self._x

并且,x.setterx.deleter装饰器是如何创建的?我很困惑。

I would like to understand how the built-in function property works. What confuses me is that property can also be used as a decorator, but it only takes arguments when used as a built-in function and not when used as a decorator.

This example is from the documentation:

class C(object):
    def __init__(self):
        self._x = None

    def getx(self):
        return self._x
    def setx(self, value):
        self._x = value
    def delx(self):
        del self._x
    x = property(getx, setx, delx, "I'm the 'x' property.")

property‘s arguments are getx, setx, delx and a doc string.

In the code below property is used as decorator. The object of it is the x function, but in the code above there is no place for an object function in the arguments.

class C(object):
    def __init__(self):
        self._x = None

    @property
    def x(self):
        """I'm the 'x' property."""
        return self._x

    @x.setter
    def x(self, value):
        self._x = value

    @x.deleter
    def x(self):
        del self._x

And, how are the x.setter and x.deleter decorators created? I am confused.


回答 0

property()函数返回一个特殊的描述符对象

>>> property()
<property object at 0x10ff07940>

正是这种对象有额外的方法:

>>> property().getter
<built-in method getter of property object at 0x10ff07998>
>>> property().setter
<built-in method setter of property object at 0x10ff07940>
>>> property().deleter
<built-in method deleter of property object at 0x10ff07998>

这些充当装饰。他们返回一个新的属性对象:

>>> property().getter(None)
<property object at 0x10ff079f0>

那是旧对象的副本,但是替换了其中一个功能。

请记住,@decorator语法只是语法糖。语法:

@property
def foo(self): return self._foo

确实与

def foo(self): return self._foo
foo = property(foo)

因此foo该函数被替换property(foo),我们在上面看到的是一个特殊的对象。然后,当您使用时@foo.setter(),您正在做的就是调用property().setter上面显示的方法,该方法将返回该属性的新副本,但是这次将setter函数替换为装饰方法。

下面的序列还通过使用那些装饰器方法创建了一个全开属性。

首先,我们property仅使用getter 创建一些函数和一个对象:

>>> def getter(self): print('Get!')
... 
>>> def setter(self, value): print('Set to {!r}!'.format(value))
... 
>>> def deleter(self): print('Delete!')
... 
>>> prop = property(getter)
>>> prop.fget is getter
True
>>> prop.fset is None
True
>>> prop.fdel is None
True

接下来,我们使用该.setter()方法添加setter:

>>> prop = prop.setter(setter)
>>> prop.fget is getter
True
>>> prop.fset is setter
True
>>> prop.fdel is None
True

最后,我们使用以下.deleter()方法添加删除器:

>>> prop = prop.deleter(deleter)
>>> prop.fget is getter
True
>>> prop.fset is setter
True
>>> prop.fdel is deleter
True

最后但并非最不重要的一点是,该property对象充当描述符对象,因此它具有和.__get__(),可以.__set__().__delete__()实例属性的获取,设置和删除方法挂钩:

>>> class Foo: pass
... 
>>> prop.__get__(Foo(), Foo)
Get!
>>> prop.__set__(Foo(), 'bar')
Set to 'bar'!
>>> prop.__delete__(Foo())
Delete!

Descriptor Howto包括以下类型的纯Python示例实现property()

class Property:
    "Emulate PyProperty_Type() in Objects/descrobject.c"

    def __init__(self, fget=None, fset=None, fdel=None, doc=None):
        self.fget = fget
        self.fset = fset
        self.fdel = fdel
        if doc is None and fget is not None:
            doc = fget.__doc__
        self.__doc__ = doc

    def __get__(self, obj, objtype=None):
        if obj is None:
            return self
        if self.fget is None:
            raise AttributeError("unreadable attribute")
        return self.fget(obj)

    def __set__(self, obj, value):
        if self.fset is None:
            raise AttributeError("can't set attribute")
        self.fset(obj, value)

    def __delete__(self, obj):
        if self.fdel is None:
            raise AttributeError("can't delete attribute")
        self.fdel(obj)

    def getter(self, fget):
        return type(self)(fget, self.fset, self.fdel, self.__doc__)

    def setter(self, fset):
        return type(self)(self.fget, fset, self.fdel, self.__doc__)

    def deleter(self, fdel):
        return type(self)(self.fget, self.fset, fdel, self.__doc__)

The property() function returns a special descriptor object:

>>> property()
<property object at 0x10ff07940>

It is this object that has extra methods:

>>> property().getter
<built-in method getter of property object at 0x10ff07998>
>>> property().setter
<built-in method setter of property object at 0x10ff07940>
>>> property().deleter
<built-in method deleter of property object at 0x10ff07998>

These act as decorators too. They return a new property object:

>>> property().getter(None)
<property object at 0x10ff079f0>

that is a copy of the old object, but with one of the functions replaced.

Remember, that the @decorator syntax is just syntactic sugar; the syntax:

@property
def foo(self): return self._foo

really means the same thing as

def foo(self): return self._foo
foo = property(foo)

so foo the function is replaced by property(foo), which we saw above is a special object. Then when you use @foo.setter(), what you are doing is call that property().setter method I showed you above, which returns a new copy of the property, but this time with the setter function replaced with the decorated method.

The following sequence also creates a full-on property, by using those decorator methods.

First we create some functions and a property object with just a getter:

>>> def getter(self): print('Get!')
... 
>>> def setter(self, value): print('Set to {!r}!'.format(value))
... 
>>> def deleter(self): print('Delete!')
... 
>>> prop = property(getter)
>>> prop.fget is getter
True
>>> prop.fset is None
True
>>> prop.fdel is None
True

Next we use the .setter() method to add a setter:

>>> prop = prop.setter(setter)
>>> prop.fget is getter
True
>>> prop.fset is setter
True
>>> prop.fdel is None
True

Last we add a deleter with the .deleter() method:

>>> prop = prop.deleter(deleter)
>>> prop.fget is getter
True
>>> prop.fset is setter
True
>>> prop.fdel is deleter
True

Last but not least, the property object acts as a descriptor object, so it has .__get__(), .__set__() and .__delete__() methods to hook into instance attribute getting, setting and deleting:

>>> class Foo: pass
... 
>>> prop.__get__(Foo(), Foo)
Get!
>>> prop.__set__(Foo(), 'bar')
Set to 'bar'!
>>> prop.__delete__(Foo())
Delete!

The Descriptor Howto includes a pure Python sample implementation of the property() type:

class Property:
    "Emulate PyProperty_Type() in Objects/descrobject.c"

    def __init__(self, fget=None, fset=None, fdel=None, doc=None):
        self.fget = fget
        self.fset = fset
        self.fdel = fdel
        if doc is None and fget is not None:
            doc = fget.__doc__
        self.__doc__ = doc

    def __get__(self, obj, objtype=None):
        if obj is None:
            return self
        if self.fget is None:
            raise AttributeError("unreadable attribute")
        return self.fget(obj)

    def __set__(self, obj, value):
        if self.fset is None:
            raise AttributeError("can't set attribute")
        self.fset(obj, value)

    def __delete__(self, obj):
        if self.fdel is None:
            raise AttributeError("can't delete attribute")
        self.fdel(obj)

    def getter(self, fget):
        return type(self)(fget, self.fset, self.fdel, self.__doc__)

    def setter(self, fset):
        return type(self)(self.fget, fset, self.fdel, self.__doc__)

    def deleter(self, fdel):
        return type(self)(self.fget, self.fset, fdel, self.__doc__)

回答 1

文档说这只是创建只读属性的捷径。所以

@property
def x(self):
    return self._x

相当于

def getx(self):
    return self._x
x = property(getx)

Documentation says it’s just a shortcut for creating readonly properties. So

@property
def x(self):
    return self._x

is equivalent to

def getx(self):
    return self._x
x = property(getx)

回答 2

这是如何@property实现的最小示例:

class Thing:
    def __init__(self, my_word):
        self._word = my_word 
    @property
    def word(self):
        return self._word

>>> print( Thing('ok').word )
'ok'

否则,将word保留方法而不是属性。

class Thing:
    def __init__(self, my_word):
        self._word = my_word
    def word(self):
        return self._word

>>> print( Thing('ok').word() )
'ok'

Here is a minimal example of how @property can be implemented:

class Thing:
    def __init__(self, my_word):
        self._word = my_word 
    @property
    def word(self):
        return self._word

>>> print( Thing('ok').word )
'ok'

Otherwise word remains a method instead of a property.

class Thing:
    def __init__(self, my_word):
        self._word = my_word
    def word(self):
        return self._word

>>> print( Thing('ok').word() )
'ok'

回答 3

第一部分很简单:

@property
def x(self): ...

是相同的

def x(self): ...
x = property(x)
  • 反过来,这是property仅使用getter 创建a的简化语法。

下一步将使用设置器和删除器扩展此属性。并通过适当的方法来实现:

@x.setter
def x(self, value): ...

返回一个新属性,该属性继承了旧属性x以及给定的setter的所有内容。

x.deleter 以相同的方式工作。

The first part is simple:

@property
def x(self): ...

is the same as

def x(self): ...
x = property(x)
  • which, in turn, is the simplified syntax for creating a property with just a getter.

The next step would be to extend this property with a setter and a deleter. And this happens with the appropriate methods:

@x.setter
def x(self, value): ...

returns a new property which inherits everything from the old x plus the given setter.

x.deleter works the same way.


回答 4

以下内容:

class C(object):
    def __init__(self):
        self._x = None

    @property
    def x(self):
        """I'm the 'x' property."""
        return self._x

    @x.setter
    def x(self, value):
        self._x = value

    @x.deleter
    def x(self):
        del self._x

是相同的:

class C(object):
    def __init__(self):
        self._x = None

    def _x_get(self):
        return self._x

    def _x_set(self, value):
        self._x = value

    def _x_del(self):
        del self._x

    x = property(_x_get, _x_set, _x_del, 
                    "I'm the 'x' property.")

是相同的:

class C(object):
    def __init__(self):
        self._x = None

    def _x_get(self):
        return self._x

    def _x_set(self, value):
        self._x = value

    def _x_del(self):
        del self._x

    x = property(_x_get, doc="I'm the 'x' property.")
    x = x.setter(_x_set)
    x = x.deleter(_x_del)

是相同的:

class C(object):
    def __init__(self):
        self._x = None

    def _x_get(self):
        return self._x
    x = property(_x_get, doc="I'm the 'x' property.")

    def _x_set(self, value):
        self._x = value
    x = x.setter(_x_set)

    def _x_del(self):
        del self._x
    x = x.deleter(_x_del)

等同于:

class C(object):
    def __init__(self):
        self._x = None

    @property
    def x(self):
        """I'm the 'x' property."""
        return self._x

    @x.setter
    def x(self, value):
        self._x = value

    @x.deleter
    def x(self):
        del self._x

This following:

class C(object):
    def __init__(self):
        self._x = None

    @property
    def x(self):
        """I'm the 'x' property."""
        return self._x

    @x.setter
    def x(self, value):
        self._x = value

    @x.deleter
    def x(self):
        del self._x

Is the same as:

class C(object):
    def __init__(self):
        self._x = None

    def _x_get(self):
        return self._x

    def _x_set(self, value):
        self._x = value

    def _x_del(self):
        del self._x

    x = property(_x_get, _x_set, _x_del, 
                    "I'm the 'x' property.")

Is the same as:

class C(object):
    def __init__(self):
        self._x = None

    def _x_get(self):
        return self._x

    def _x_set(self, value):
        self._x = value

    def _x_del(self):
        del self._x

    x = property(_x_get, doc="I'm the 'x' property.")
    x = x.setter(_x_set)
    x = x.deleter(_x_del)

Is the same as:

class C(object):
    def __init__(self):
        self._x = None

    def _x_get(self):
        return self._x
    x = property(_x_get, doc="I'm the 'x' property.")

    def _x_set(self, value):
        self._x = value
    x = x.setter(_x_set)

    def _x_del(self):
        del self._x
    x = x.deleter(_x_del)

Which is the same as :

class C(object):
    def __init__(self):
        self._x = None

    @property
    def x(self):
        """I'm the 'x' property."""
        return self._x

    @x.setter
    def x(self, value):
        self._x = value

    @x.deleter
    def x(self):
        del self._x

回答 5

下面是另一个示例,该示例在@property需要重构代码的情况下如何提供帮助(从此处进行总结):

假设您创建了一个Money这样的类:

class Money:
    def __init__(self, dollars, cents):
        self.dollars = dollars
        self.cents = cents

并且用户根据他/她使用的此类创建一个库

money = Money(27, 12)

print("I have {} dollar and {} cents.".format(money.dollars, money.cents))
# prints I have 27 dollar and 12 cents.

现在,让我们假设您决定更改您的Money类并摆脱dollarscents属性,而是决定仅跟踪总分:

class Money:
    def __init__(self, dollars, cents):
        self.total_cents = dollars * 100 + cents

如果上述用户现在尝试像以前一样运行他/她的库

money = Money(27, 12)

print("I have {} dollar and {} cents.".format(money.dollars, money.cents))

这会导致错误

AttributeError:“ Money”对象没有属性“ dollars”

也就是说,现在大家谁依赖于原始的手段Money类将不得不改变所有代码行,其中dollarscents使用可以是非常痛苦……那么,怎么会这样避免?通过使用@property

就是那样:

class Money:
    def __init__(self, dollars, cents):
        self.total_cents = dollars * 100 + cents

    # Getter and setter for dollars...
    @property
    def dollars(self):
        return self.total_cents // 100

    @dollars.setter
    def dollars(self, new_dollars):
        self.total_cents = 100 * new_dollars + self.cents

    # And the getter and setter for cents.
    @property
    def cents(self):
        return self.total_cents % 100

    @cents.setter
    def cents(self, new_cents):
        self.total_cents = 100 * self.dollars + new_cents

现在我们从图书馆打电话时

money = Money(27, 12)

print("I have {} dollar and {} cents.".format(money.dollars, money.cents))
# prints I have 27 dollar and 12 cents.

它会按预期工作,我们不必在库中更改任何代码!实际上,我们甚至不必知道我们依赖的库已更改。

setter可以正常工作:

money.dollars += 2
print("I have {} dollar and {} cents.".format(money.dollars, money.cents))
# prints I have 29 dollar and 12 cents.

money.cents += 10
print("I have {} dollar and {} cents.".format(money.dollars, money.cents))
# prints I have 29 dollar and 22 cents.

您也@property可以在抽象类中使用。我在这里举一个最小的例子。

Below is another example on how @property can help when one has to refactor code which is taken from here (I only summarize it below):

Imagine you created a class Money like this:

class Money:
    def __init__(self, dollars, cents):
        self.dollars = dollars
        self.cents = cents

and an user creates a library depending on this class where he/she uses e.g.

money = Money(27, 12)

print("I have {} dollar and {} cents.".format(money.dollars, money.cents))
# prints I have 27 dollar and 12 cents.

Now let’s suppose you decide to change your Money class and get rid of the dollars and cents attributes but instead decide to only track the total amount of cents:

class Money:
    def __init__(self, dollars, cents):
        self.total_cents = dollars * 100 + cents

If the above mentioned user now tries to run his/her library as before

money = Money(27, 12)

print("I have {} dollar and {} cents.".format(money.dollars, money.cents))

it will result in an error

AttributeError: ‘Money’ object has no attribute ‘dollars’

That means that now everyone who relies on your original Money class would have to change all lines of code where dollars and cents are used which can be very painful… So, how could this be avoided? By using @property!

That is how:

class Money:
    def __init__(self, dollars, cents):
        self.total_cents = dollars * 100 + cents

    # Getter and setter for dollars...
    @property
    def dollars(self):
        return self.total_cents // 100

    @dollars.setter
    def dollars(self, new_dollars):
        self.total_cents = 100 * new_dollars + self.cents

    # And the getter and setter for cents.
    @property
    def cents(self):
        return self.total_cents % 100

    @cents.setter
    def cents(self, new_cents):
        self.total_cents = 100 * self.dollars + new_cents

when we now call from our library

money = Money(27, 12)

print("I have {} dollar and {} cents.".format(money.dollars, money.cents))
# prints I have 27 dollar and 12 cents.

it will work as expected and we did not have to change a single line of code in our library! In fact, we would not even have to know that the library we depend on changed.

Also the setter works fine:

money.dollars += 2
print("I have {} dollar and {} cents.".format(money.dollars, money.cents))
# prints I have 29 dollar and 12 cents.

money.cents += 10
print("I have {} dollar and {} cents.".format(money.dollars, money.cents))
# prints I have 29 dollar and 22 cents.

You can use @property also in abstract classes; I give a minimal example here.


回答 6

我在这里阅读了所有文章,并意识到我们可能需要一个真实的例子。为什么实际上我们有@property?因此,考虑使用身份验证系统的Flask应用。您在中声明模型用户models.py

class User(UserMixin, db.Model):
    __tablename__ = 'users'
    id = db.Column(db.Integer, primary_key=True)
    email = db.Column(db.String(64), unique=True, index=True)
    username = db.Column(db.String(64), unique=True, index=True)
    password_hash = db.Column(db.String(128))

    ...

    @property
    def password(self):
        raise AttributeError('password is not a readable attribute')

    @password.setter
    def password(self, password):
        self.password_hash = generate_password_hash(password)

    def verify_password(self, password):
        return check_password_hash(self.password_hash, password)

在这段代码中,我们password使用了“隐藏”属性,当您尝试直接访问它时@property,该属性会触发AttributeError断言,而我们使用@ property.setter来设置实际的实例变量password_hash

现在,auth/views.py我们可以实例化一个用户:

...
@auth.route('/register', methods=['GET', 'POST'])
def register():
    form = RegisterForm()
    if form.validate_on_submit():
        user = User(email=form.email.data,
                    username=form.username.data,
                    password=form.password.data)
        db.session.add(user)
        db.session.commit()
...

password用户填写表单时,该属性来自注册表单。密码确认发生在前端EqualTo('password', message='Passwords must match')(如果您想知道,但这是与Flask表单相关的其他主题)。

我希望这个例子会有用

I read all the posts here and realized that we may need a real life example. Why, actually, we have @property? So, consider a Flask app where you use authentication system. You declare a model User in models.py:

class User(UserMixin, db.Model):
    __tablename__ = 'users'
    id = db.Column(db.Integer, primary_key=True)
    email = db.Column(db.String(64), unique=True, index=True)
    username = db.Column(db.String(64), unique=True, index=True)
    password_hash = db.Column(db.String(128))

    ...

    @property
    def password(self):
        raise AttributeError('password is not a readable attribute')

    @password.setter
    def password(self, password):
        self.password_hash = generate_password_hash(password)

    def verify_password(self, password):
        return check_password_hash(self.password_hash, password)

In this code we’ve “hidden” attribute password by using @property which triggers AttributeError assertion when you try to access it directly, while we used @property.setter to set the actual instance variable password_hash.

Now in auth/views.py we can instantiate a User with:

...
@auth.route('/register', methods=['GET', 'POST'])
def register():
    form = RegisterForm()
    if form.validate_on_submit():
        user = User(email=form.email.data,
                    username=form.username.data,
                    password=form.password.data)
        db.session.add(user)
        db.session.commit()
...

Notice attribute password that comes from a registration form when a user fills the form. Password confirmation happens on the front end with EqualTo('password', message='Passwords must match') (in case if you are wondering, but it’s a different topic related Flask forms).

I hope this example will be useful


回答 7

那里的很多人都清楚了这一点,但这是我一直在寻找的直接点。我觉得从@property装饰器开始很重要。例如:-

class UtilityMixin():
    @property
    def get_config(self):
        return "This is property"

函数“ get_config()”的调用将像这样工作。

util = UtilityMixin()
print(util.get_config)

如果您注意到我没有使用“()”括号来调用该函数。这是我在搜索@property装饰器的基本内容。这样您就可以像使用变量一样使用函数。

This point is been cleared by many people up there but here is a direct point which I was searching. This is what I feel is important to start with the @property decorator. eg:-

class UtilityMixin():
    @property
    def get_config(self):
        return "This is property"

The calling of function “get_config()” will work like this.

util = UtilityMixin()
print(util.get_config)

If you notice I have not used “()” brackets for calling the function. This is the basic thing which I was searching for the @property decorator. So that you can use your function just like a variable.


回答 8

让我们从Python装饰器开始。

Python装饰器是一个函数,可以帮助向已经定义的函数添加一些其他功能。

在Python中,一切都是对象。Python中的函数是一流的对象,这意味着它们可以被变量引用,添加到列表中,作为参数传递给另一个函数等。

考虑以下代码片段。

def decorator_func(fun):
    def wrapper_func():
        print("Wrapper function started")
        fun()
        print("Given function decorated")
        # Wrapper function add something to the passed function and decorator 
        # returns the wrapper function
    return wrapper_func

def say_bye():
    print("bye!!")

say_bye = decorator_func(say_bye)
say_bye()

# Output:
#  Wrapper function started
#  bye
#  Given function decorated

在这里,我们可以说装饰器函数修改了我们的say_hello函数,并在其中添加了一些额外的代码行。

装饰器的Python语法

def decorator_func(fun):
    def wrapper_func():
        print("Wrapper function started")
        fun()
        print("Given function decorated")
        # Wrapper function add something to the passed function and decorator 
        # returns the wrapper function
    return wrapper_func

@decorator_func
def say_bye():
    print("bye!!")

say_bye()

最后,让我们结束一个案例案例,但在此之前,让我们先讨论一些糟糕的原则。

在许多面向对象的编程语言中都使用getter和setter来确保数据封装的原理(被视为将数据与对这些数据进行操作的方法捆绑在一起)。

这些方法当然是用于获取数据的吸气剂和用于更改数据的设置器。

根据此原理,将一个类的属性设为私有,以隐藏它们并保护它们免受其他代码的侵害。

是的,@ property基本上是使用getter和setterpythonic方法。

Python有一个伟大的概念,称为属性,它使面向对象的程序员的生活变得更加简单。

让我们假设您决定创建一个可以存储摄氏温度的类。

class Celsius:
def __init__(self, temperature = 0):
    self.set_temperature(temperature)

def to_fahrenheit(self):
    return (self.get_temperature() * 1.8) + 32

def get_temperature(self):
    return self._temperature

def set_temperature(self, value):
    if value < -273:
        raise ValueError("Temperature below -273 is not possible")
    self._temperature = value

重构代码,这是我们可以通过属性实现的方法。

在Python中,property()是一个内置函数,可创建并返回属性对象。

属性对象具有三种方法,getter(),setter()和delete()。

class Celsius:
def __init__(self, temperature = 0):
    self.temperature = temperature

def to_fahrenheit(self):
    return (self.temperature * 1.8) + 32

def get_temperature(self):
    print("Getting value")
    return self.temperature

def set_temperature(self, value):
    if value < -273:
        raise ValueError("Temperature below -273 is not possible")
    print("Setting value")
    self.temperature = value

temperature = property(get_temperature,set_temperature)

这里,

temperature = property(get_temperature,set_temperature)

本可以分解为

# make empty property
temperature = property()
# assign fget
temperature = temperature.getter(get_temperature)
# assign fset
temperature = temperature.setter(set_temperature)

注意事项:

  • get_temperature仍然是属性而不是方法。

现在,您可以通过写入来获取温度值。

C = Celsius()
C.temperature
# instead of writing C.get_temperature()

我们可以进一步继续,不要定义名称get_temperatureset_temperature,因为它们是不必要的,并污染类命名空间。

解决上述问题的pythonic方法是使用@property

class Celsius:
    def __init__(self, temperature = 0):
        self.temperature = temperature

    def to_fahrenheit(self):
        return (self.temperature * 1.8) + 32

    @property
    def temperature(self):
        print("Getting value")
        return self.temperature

    @temperature.setter
    def temperature(self, value):
        if value < -273:
            raise ValueError("Temperature below -273 is not possible")
        print("Setting value")
        self.temperature = value

注意事项-

  1. 用于获取值的方法以“ @property”修饰。
  2. 用作设置器的方法用“ @ temperature.setter”修饰,如果该函数被称为“ x”,则必须用“ @ x.setter”修饰。
  3. 我们用相同的名称和不同数量的参数“ def temperature(self)”和“ def temperature(self,x)”编写了“两个”方法。

如您所见,该代码绝对不太优雅。

现在,让我们谈谈一个现实的实用场景。

假设您设计的类如下:

class OurClass:

    def __init__(self, a):
        self.x = a


y = OurClass(10)
print(y.x)

现在,让我们进一步假设我们的类在客户中很受欢迎,并且他们开始在程序中使用它。他们对对象进行了各种分配。

有朝一日,一个值得信赖的客户来找我们,建议“ x”的值必须在0到1000之间,这确实是一个可怕的情况!

由于属性,这很容易:我们创建属性版本“ x”。

class OurClass:

    def __init__(self,x):
        self.x = x

    @property
    def x(self):
        return self.__x

    @x.setter
    def x(self, x):
        if x < 0:
            self.__x = 0
        elif x > 1000:
            self.__x = 1000
        else:
            self.__x = x

很好,不是吗:您可以从可以想象到的最简单的实现开始,并且以后可以随意迁移到属性版本,而不必更改接口!因此,属性不仅仅是吸气剂和塞特剂的替代品!

您可以在此处检查此实现

Let’s start with Python decorators.

A Python decorator is a function that helps to add some additional functionalities to an already defined function.

In Python, everything is an object. Functions in Python are first-class objects which means that they can be referenced by a variable, added in the lists, passed as arguments to another function etc.

Consider the following code snippet.

def decorator_func(fun):
    def wrapper_func():
        print("Wrapper function started")
        fun()
        print("Given function decorated")
        # Wrapper function add something to the passed function and decorator 
        # returns the wrapper function
    return wrapper_func

def say_bye():
    print("bye!!")

say_bye = decorator_func(say_bye)
say_bye()

# Output:
#  Wrapper function started
#  bye
#  Given function decorated

Here, we can say that decorator function modified our say_hello function and added some extra lines of code in it.

Python syntax for decorator

def decorator_func(fun):
    def wrapper_func():
        print("Wrapper function started")
        fun()
        print("Given function decorated")
        # Wrapper function add something to the passed function and decorator 
        # returns the wrapper function
    return wrapper_func

@decorator_func
def say_bye():
    print("bye!!")

say_bye()

Let’s Concluded everything than with a case scenario, but before that let’s talk about some oops priniciples.

Getters and setters are used in many object oriented programming languages to ensure the principle of data encapsulation(is seen as the bundling of data with the methods that operate on these data.)

These methods are of course the getter for retrieving the data and the setter for changing the data.

According to this principle, the attributes of a class are made private to hide and protect them from other code.

Yup, @property is basically a pythonic way to use getters and setters.

Python has a great concept called property which makes the life of an object-oriented programmer much simpler.

Let us assume that you decide to make a class that could store the temperature in degree Celsius.

class Celsius:
def __init__(self, temperature = 0):
    self.set_temperature(temperature)

def to_fahrenheit(self):
    return (self.get_temperature() * 1.8) + 32

def get_temperature(self):
    return self._temperature

def set_temperature(self, value):
    if value < -273:
        raise ValueError("Temperature below -273 is not possible")
    self._temperature = value

Refactored Code, Here is how we could have achieved it with property.

In Python, property() is a built-in function that creates and returns a property object.

A property object has three methods, getter(), setter(), and delete().

class Celsius:
def __init__(self, temperature = 0):
    self.temperature = temperature

def to_fahrenheit(self):
    return (self.temperature * 1.8) + 32

def get_temperature(self):
    print("Getting value")
    return self.temperature

def set_temperature(self, value):
    if value < -273:
        raise ValueError("Temperature below -273 is not possible")
    print("Setting value")
    self.temperature = value

temperature = property(get_temperature,set_temperature)

Here,

temperature = property(get_temperature,set_temperature)

could have been broken down as,

# make empty property
temperature = property()
# assign fget
temperature = temperature.getter(get_temperature)
# assign fset
temperature = temperature.setter(set_temperature)

Point To Note:

  • get_temperature remains a property instead of a method.

Now you can access the value of temperature by writing.

C = Celsius()
C.temperature
# instead of writing C.get_temperature()

We can further go on and not define names get_temperature and set_temperature as they are unnecessary and pollute the class namespace.

The pythonic way to deal with the above problem is to use @property.

class Celsius:
    def __init__(self, temperature = 0):
        self.temperature = temperature

    def to_fahrenheit(self):
        return (self.temperature * 1.8) + 32

    @property
    def temperature(self):
        print("Getting value")
        return self.temperature

    @temperature.setter
    def temperature(self, value):
        if value < -273:
            raise ValueError("Temperature below -273 is not possible")
        print("Setting value")
        self.temperature = value

Points to Note –

  1. A method which is used for getting a value is decorated with “@property”.
  2. The method which has to function as the setter is decorated with “@temperature.setter”, If the function had been called “x”, we would have to decorate it with “@x.setter”.
  3. We wrote “two” methods with the same name and a different number of parameters “def temperature(self)” and “def temperature(self,x)”.

As you can see, the code is definitely less elegant.

Now,let’s talk about one real-life practical scenerio.

Let’s say you have designed a class as follows:

class OurClass:

    def __init__(self, a):
        self.x = a


y = OurClass(10)
print(y.x)

Now, let’s further assume that our class got popular among clients and they started using it in their programs, They did all kinds of assignments to the object.

And One fateful day, a trusted client came to us and suggested that “x” has to be a value between 0 and 1000, this is really a horrible scenario!

Due to properties it’s easy: We create a property version of “x”.

class OurClass:

    def __init__(self,x):
        self.x = x

    @property
    def x(self):
        return self.__x

    @x.setter
    def x(self, x):
        if x < 0:
            self.__x = 0
        elif x > 1000:
            self.__x = 1000
        else:
            self.__x = x

This is great, isn’t it: You can start with the simplest implementation imaginable, and you are free to later migrate to a property version without having to change the interface! So properties are not just a replacement for getters and setter!

You can check this Implementation here


回答 9

property@property装饰器背后的一类。

您可以随时检查以下内容:

print(property) #<class 'property'>

我改写了示例,help(property)以显示@property语法

class C:
    def __init__(self):
        self._x=None

    @property 
    def x(self):
        return self._x

    @x.setter 
    def x(self, value):
        self._x = value

    @x.deleter
    def x(self):
        del self._x

c = C()
c.x="a"
print(c.x)

在功能上与property()语法相同:

class C:
    def __init__(self):
        self._x=None

    def g(self):
        return self._x

    def s(self, v):
        self._x = v

    def d(self):
        del self._x

    prop = property(g,s,d)

c = C()
c.x="a"
print(c.x)

如您所见,我们使用该属性的方式没有什么不同。

为了回答这个问题,@property装饰器是通过property类实现的。


因此,问题是要对该property类进行一些解释。这行:

prop = property(g,s,d)

是初始化。我们可以这样重写它:

prop = property(fget=g,fset=s,fdel=d)

的含义fgetfsetfdel

 |    fget
 |      function to be used for getting an attribute value
 |    fset
 |      function to be used for setting an attribute value
 |    fdel
 |      function to be used for del'ing an attribute
 |    doc
 |      docstring

下图显示了我们从类中获得的三胞胎property

在此处输入图片说明

__get____set____delete__那里被覆盖。这是Python中描述符模式的实现。

通常,描述符是具有“绑定行为”的对象属性,其属性访问已被描述符协议中的方法所覆盖。

我们还可以使用属性settergetterdeleter方法的功能绑定属性。检查下一个示例。s2该类的方法C会将属性设置为double

class C:
    def __init__(self):
        self._x=None

    def g(self):
        return self._x

    def s(self, x):
        self._x = x

    def d(self):
        del self._x

    def s2(self,x):
        self._x=x+x


    x=property(g)
    x=x.setter(s)
    x=x.deleter(d)      


c = C()
c.x="a"
print(c.x) # outputs "a"

C.x=property(C.g, C.s2)
C.x=C.x.deleter(C.d)
c2 = C()
c2.x="a"
print(c2.x) # outputs "aa"

property is a class behind @property decorator.

You can always check this:

print(property) #<class 'property'>

I rewrote the example from help(property) to show that the @property syntax

class C:
    def __init__(self):
        self._x=None

    @property 
    def x(self):
        return self._x

    @x.setter 
    def x(self, value):
        self._x = value

    @x.deleter
    def x(self):
        del self._x

c = C()
c.x="a"
print(c.x)

is functionally identical to property() syntax:

class C:
    def __init__(self):
        self._x=None

    def g(self):
        return self._x

    def s(self, v):
        self._x = v

    def d(self):
        del self._x

    prop = property(g,s,d)

c = C()
c.x="a"
print(c.x)

There is no difference how we use the property as you can see.

To answer the question @property decorator is implemented via property class.


So, the question is to explain the property class a bit. This line:

prop = property(g,s,d)

Was the initialization. We can rewrite it like this:

prop = property(fget=g,fset=s,fdel=d)

The meaning of fget, fset and fdel:

 |    fget
 |      function to be used for getting an attribute value
 |    fset
 |      function to be used for setting an attribute value
 |    fdel
 |      function to be used for del'ing an attribute
 |    doc
 |      docstring

The next image shows the triplets we have, from the class property:

enter image description here

__get__, __set__, and __delete__ are there to be overridden. This is the implementation of the descriptor pattern in Python.

In general, a descriptor is an object attribute with “binding behavior”, one whose attribute access has been overridden by methods in the descriptor protocol.

We can also use property setter, getter and deleter methods to bind the function to property. Check the next example. The method s2 of the class C will set the property doubled.

class C:
    def __init__(self):
        self._x=None

    def g(self):
        return self._x

    def s(self, x):
        self._x = x

    def d(self):
        del self._x

    def s2(self,x):
        self._x=x+x


    x=property(g)
    x=x.setter(s)
    x=x.deleter(d)      


c = C()
c.x="a"
print(c.x) # outputs "a"

C.x=property(C.g, C.s2)
C.x=C.x.deleter(C.d)
c2 = C()
c2.x="a"
print(c2.x) # outputs "aa"

回答 10

可以通过两种方式声明属性。

  • 为属性创建getter,setter方法,然后将它们作为参数传递给属性函数
  • 使用@property装饰器。

您可以看一下我编写的有关python属性的一些示例。

A property can be declared in two ways.

  • Creating the getter, setter methods for an attribute and then passing these as argument to property function
  • Using the @property decorator.

You can have a look at few examples I have written about properties in python.


回答 11

最好的解释可以在这里找到:Python @Property Explained –如何使用和何时使用?(完整示例)Selva Prabhakaran | 发表于十一月5,2018

它帮助我理解了为什么不仅如此。

https://www.machinelearningplus.com/python/python-property/

The best explanation can be found here: Python @Property Explained – How to Use and When? (Full Examples) by Selva Prabhakaran | Posted on November 5, 2018

It helped me understand WHY not only HOW.

https://www.machinelearningplus.com/python/python-property/


回答 12

这是另一个示例:

##
## Python Properties Example
##
class GetterSetterExample( object ):
    ## Set the default value for x ( we reference it using self.x, set a value using self.x = value )
    __x = None


##
## On Class Initialization - do something... if we want..
##
def __init__( self ):
    ## Set a value to __x through the getter / setter... Since __x is defined above, this doesn't need to be set...
    self.x = 1234

    return None


##
## Define x as a property, ie a getter - All getters should have a default value arg, so I added it - it will not be passed in when setting a value, so you need to set the default here so it will be used..
##
@property
def x( self, _default = None ):
    ## I added an optional default value argument as all getters should have this - set it to the default value you want to return...
    _value = ( self.__x, _default )[ self.__x == None ]

    ## Debugging - so you can see the order the calls are made...
    print( '[ Test Class ] Get x = ' + str( _value ) )

    ## Return the value - we are a getter afterall...
    return _value


##
## Define the setter function for x...
##
@x.setter
def x( self, _value = None ):
    ## Debugging - so you can see the order the calls are made...
    print( '[ Test Class ] Set x = ' + str( _value ) )

    ## This is to show the setter function works.... If the value is above 0, set it to a negative value... otherwise keep it as is ( 0 is the only non-negative number, it can't be negative or positive anyway )
    if ( _value > 0 ):
        self.__x = -_value
    else:
        self.__x = _value


##
## Define the deleter function for x...
##
@x.deleter
def x( self ):
    ## Unload the assignment / data for x
    if ( self.__x != None ):
        del self.__x


##
## To String / Output Function for the class - this will show the property value for each property we add...
##
def __str__( self ):
    ## Output the x property data...
    print( '[ x ] ' + str( self.x ) )


    ## Return a new line - technically we should return a string so it can be printed where we want it, instead of printed early if _data = str( C( ) ) is used....
    return '\n'

##
##
##
_test = GetterSetterExample( )
print( _test )

## For some reason the deleter isn't being called...
del _test.x

基本上,与C(object)示例相同,只是我改用x …我也不在__init中初始化 -…很好..我可以,但是可以删除它,因为__x被定义为一部分班上的…

输出为:

[ Test Class ] Set x = 1234
[ Test Class ] Get x = -1234
[ x ] -1234

如果我将init的self.x = 1234注释掉,则输出为:

[ Test Class ] Get x = None
[ x ] None

并且如果我在getter函数中将_default = None设置为_default = 0(因为所有的getter都应具有默认值,但不会被我所看到的属性值传递,因此您可以在此处定义它,以及它实际上还不错,因为您可以定义一次默认值并在所有地方使用它),即:def x(self,_default = 0):

[ Test Class ] Get x = 0
[ x ] 0

注意:getter逻辑只是为了让它操纵值以确保它被操纵-与print语句相同…

注意:我习惯了Lua,并且在调用单个函数时能够动态创建10个以上的助手,并且我在不使用属性的情况下为Python做了类似的事情,并且在一定程度上可以正常工作,但是,即使这些函数是在之前创建的被使用时,在创建它们之前有时仍会调用它们,这很奇怪,因为它不是以这种方式编码的。。。我更喜欢Lua元表的灵活性,而且我可以使用实际的setter / getters。而不是本质上直接访问变量…我确实喜欢用Python可以快速构建某些东西-例如gui程序。尽管没有大量其他库,虽然我正在设计的库可能无法实现-如果我在AutoHotkey中对其进行编码,则可以直接访问所需的dll调用,并且可以在Java,C#,C ++,

注意:此论坛中的代码输出已损坏-我必须在代码的第一部分中添加空格才能使其正常工作-复制/粘贴时,请确保将所有空格都转换为制表符…。我在Python中使用制表符,因为在10,000行的文件大小可以为512KB至1MB(带空格)和100至200KB(带制表符),这在文件大小和减少处理时间方面存在巨大差异。

还可以按用户调整选项卡-因此,如果您希望使用2个空格宽度,4个,8个空格或您可以做的任何事情,这意味着它对于有视力缺陷的开发人员来说是体贴的。

注意:由于论坛软件中的错误,该类中定义的所有功能均未正确缩进-如果复制/粘贴,请确保将其缩进

Here is another example:

##
## Python Properties Example
##
class GetterSetterExample( object ):
    ## Set the default value for x ( we reference it using self.x, set a value using self.x = value )
    __x = None


##
## On Class Initialization - do something... if we want..
##
def __init__( self ):
    ## Set a value to __x through the getter / setter... Since __x is defined above, this doesn't need to be set...
    self.x = 1234

    return None


##
## Define x as a property, ie a getter - All getters should have a default value arg, so I added it - it will not be passed in when setting a value, so you need to set the default here so it will be used..
##
@property
def x( self, _default = None ):
    ## I added an optional default value argument as all getters should have this - set it to the default value you want to return...
    _value = ( self.__x, _default )[ self.__x == None ]

    ## Debugging - so you can see the order the calls are made...
    print( '[ Test Class ] Get x = ' + str( _value ) )

    ## Return the value - we are a getter afterall...
    return _value


##
## Define the setter function for x...
##
@x.setter
def x( self, _value = None ):
    ## Debugging - so you can see the order the calls are made...
    print( '[ Test Class ] Set x = ' + str( _value ) )

    ## This is to show the setter function works.... If the value is above 0, set it to a negative value... otherwise keep it as is ( 0 is the only non-negative number, it can't be negative or positive anyway )
    if ( _value > 0 ):
        self.__x = -_value
    else:
        self.__x = _value


##
## Define the deleter function for x...
##
@x.deleter
def x( self ):
    ## Unload the assignment / data for x
    if ( self.__x != None ):
        del self.__x


##
## To String / Output Function for the class - this will show the property value for each property we add...
##
def __str__( self ):
    ## Output the x property data...
    print( '[ x ] ' + str( self.x ) )


    ## Return a new line - technically we should return a string so it can be printed where we want it, instead of printed early if _data = str( C( ) ) is used....
    return '\n'

##
##
##
_test = GetterSetterExample( )
print( _test )

## For some reason the deleter isn't being called...
del _test.x

Basically, the same as the C( object ) example except I’m using x instead… I also don’t initialize in __init – … well.. I do, but it can be removed because __x is defined as part of the class….

The output is:

[ Test Class ] Set x = 1234
[ Test Class ] Get x = -1234
[ x ] -1234

and if I comment out the self.x = 1234 in init then the output is:

[ Test Class ] Get x = None
[ x ] None

and if I set the _default = None to _default = 0 in the getter function ( as all getters should have a default value but it isn’t passed in by the property values from what I’ve seen so you can define it here, and it actually isn’t bad because you can define the default once and use it everywhere ) ie: def x( self, _default = 0 ):

[ Test Class ] Get x = 0
[ x ] 0

Note: The getter logic is there just to have the value be manipulated by it to ensure it is manipulated by it – the same for the print statements…

Note: I’m used to Lua and being able to dynamically create 10+ helpers when I call a single function and I made something similar for Python without using properties and it works to a degree, but, even though the functions are being created before being used, there are still issues at times with them being called prior to being created which is strange as it isn’t coded that way… I prefer the flexibility of Lua meta-tables and the fact I can use actual setters / getters instead of essentially directly accessing a variable… I do like how quickly some things can be built with Python though – for instance gui programs. although one I am designing may not be possible without a lot of additional libraries – if I code it in AutoHotkey I can directly access the dll calls I need, and the same can be done in Java, C#, C++, and more – maybe I haven’t found the right thing yet but for that project I may switch from Python..

Note: The code output in this forum is broken – I had to add spaces to the first part of the code for it to work – when copy / pasting ensure you convert all spaces to tabs…. I use tabs for Python because in a file which is 10,000 lines the filesize can be 512KB to 1MB with spaces and 100 to 200KB with tabs which equates to a massive difference for file size, and reduction in processing time…

Tabs can also be adjusted per user – so if you prefer 2 spaces width, 4, 8 or whatever you can do it meaning it is thoughtful for developers with eye-sight deficits.

Note: All of the functions defined in the class aren’t indented properly because of a bug in the forum software – ensure you indent it if you copy / paste


回答 13

一句话:对我来说,对于Python 2.x,@property当我不继承自object

class A():
    pass

但在以下情况下有效:

class A(object):
    pass

对于Python 3,始终有效。

One remark: for me, for Python 2.x, @property didn’t work as advertised when I didn’t inherit from object:

class A():
    pass

but worked when:

class A(object):
    pass

for Python 3, worked always.


使用@property与getter和setter

问题:使用@property与getter和setter

这是一个纯Python特定的设计问题:

class MyClass(object):
    ...
    def get_my_attr(self):
        ...

    def set_my_attr(self, value):
        ...

class MyClass(object):
    ...        
    @property
    def my_attr(self):
        ...

    @my_attr.setter
    def my_attr(self, value):
        ...

Python让我们可以以任何一种方式来做。如果要设计Python程序,将使用哪种方法,为什么?

Here is a pure Python-specific design question:

class MyClass(object):
    ...
    def get_my_attr(self):
        ...

    def set_my_attr(self, value):
        ...

and

class MyClass(object):
    ...        
    @property
    def my_attr(self):
        ...

    @my_attr.setter
    def my_attr(self, value):
        ...

Python lets us to do it either way. If you would design a Python program, which approach would you use and why?


回答 0

首选属性。这就是他们在那里的目的。

原因是所有属性在Python中都是公共的。以一两个下划线开头的名称只是警告,给定属性是实现细节,在将来的代码版本中可能会保持不变。它不会阻止您实际获取或设置该属性。因此,标准属性访问是访问属性的常规Python方式。

属性的优点是它们在语法上与属性访问相同,因此您可以在不更改客户端代码的情况下从一个属性更改为另一个属性。您甚至可以拥有使用属性的类的一个版本(例如,用于按合同进行代码或调试),而不用于生产的版本,而无需更改使用该属性的代码。同时,您不必为所有内容编写getter和setter,以防万一您以后可能需要更好地控制访问。

Prefer properties. It’s what they’re there for.

The reason is that all attributes are public in Python. Starting names with an underscore or two is just a warning that the given attribute is an implementation detail that may not stay the same in future versions of the code. It doesn’t prevent you from actually getting or setting that attribute. Therefore, standard attribute access is the normal, Pythonic way of, well, accessing attributes.

The advantage of properties is that they are syntactically identical to attribute access, so you can change from one to another without any changes to client code. You could even have one version of a class that uses properties (say, for code-by-contract or debugging) and one that doesn’t for production, without changing the code that uses it. At the same time, you don’t have to write getters and setters for everything just in case you might need to better control access later.


回答 1

在Python中,您不会仅仅为了获得乐趣而使用getter,setter或属性。首先,您只使用属性,然后,仅在需要时才最终将其迁移到属性,而不必使用类更改代码。

确实有很多扩展名为.py的代码,它们在任何地方(例如,简单的元组)都可以使用getter和setters以及继承和无意义的类,但这是人们使用Python用C ++或Java编写的。

那不是Python代码。

In Python you don’t use getters or setters or properties just for the fun of it. You first just use attributes and then later, only if needed, eventually migrate to a property without having to change the code using your classes.

There is indeed a lot of code with extension .py that uses getters and setters and inheritance and pointless classes everywhere where e.g. a simple tuple would do, but it’s code from people writing in C++ or Java using Python.

That’s not Python code.


回答 2

使用属性可以使您从普通的属性访问开始,然后在必要时使用getter和setter备份它们

Using properties lets you begin with normal attribute accesses and then back them up with getters and setters afterwards as necessary.


回答 3

简短的答案是:属性胜出。总是。

有时有时需要吸气剂和吸气剂,但即使那样,我仍会将其“隐藏”到外界。有很多方法在Python做到这一点(getattrsetattr__getattribute__,等等,但一个非常简洁和干净的一个是:

def set_email(self, value):
    if '@' not in value:
        raise Exception("This doesn't look like an email address.")
    self._email = value

def get_email(self):
    return self._email

email = property(get_email, set_email)

这是一篇简短的文章,介绍Python中的getter和setter主题。

The short answer is: properties wins hands down. Always.

There is sometimes a need for getters and setters, but even then, I would “hide” them to the outside world. There are plenty of ways to do this in Python (getattr, setattr, __getattribute__, etc…, but a very concise and clean one is:

def set_email(self, value):
    if '@' not in value:
        raise Exception("This doesn't look like an email address.")
    self._email = value

def get_email(self):
    return self._email

email = property(get_email, set_email)

Here’s a brief article that introduces the topic of getters and setters in Python.


回答 4

[ TL; DR? 您可以跳到最后一个代码示例。]

实际上,我更喜欢使用另一种习惯用法,这是一个单独的习惯,但是如果您有一个更复杂的用例,那就很好了。

首先有一些背景知识。

属性是有用的,因为它们允许我们以编程方式处理设置和获取值,但仍允许将属性作为属性进行访问。我们可以(基本上)将“获取”转换为“计算”,并且可以将“设置”转换为“事件”。假设我们有以下类,我已经使用类似Java的getter和setter进行了编码。

class Example(object):
    def __init__(self, x=None, y=None):
        self.x = x
        self.y = y

    def getX(self):
        return self.x or self.defaultX()

    def getY(self):
        return self.y or self.defaultY()

    def setX(self, x):
        self.x = x

    def setY(self, y):
        self.y = y

    def defaultX(self):
        return someDefaultComputationForX()

    def defaultY(self):
        return someDefaultComputationForY()

你可能会奇怪,为什么我没有打电话defaultX,并defaultY在对象的__init__方法。原因是,对于我们的情况,我想假设这些someDefaultComputation方法返回的值会随时间变化,例如时间戳记,以及未设置(xy)的时间(在本示例中,“未设置”的意思是“已设置”到无“)我想的值x的(或y的)默认计算。

因此,由于上述多种原因,这是la脚的。我将使用属性重写它:

class Example(object):
    def __init__(self, x=None, y=None):
        self._x = x
        self._y = y

    @property
    def x(self):
        return self.x or self.defaultX()

    @x.setter
    def x(self, value):
        self._x = value

    @property
    def y(self):
        return self.y or self.defaultY()

    @y.setter
    def y(self, value):
        self._y = value

    # default{XY} as before.

我们获得了什么?我们已经拥有将这些属性称为属性的能力,即使我们在后台最终运行了方法。

当然,属性的真正威力在于,我们通常希望这些方法除了获取和设置值外还可以做一些事情(否则,使用属性毫无意义)。我在我的getter示例中做到了这一点。基本上,我们正在运行一个函数主体以在未设置任何值时获取默认值。这是非常常见的模式。

但是,我们正在失去什么,我们不能做什么?

在我看来,主要的烦恼是,如果您定义了一个吸气剂(就像我们在这里所做的那样),那么您还必须定义一个setter。[1] 那是使代码混乱的额外噪音。

另一个烦人的地方是,我们仍然必须初始化中的xy__init__。(当然,我们可以使用添加它们,setattr()但这是更多的额外代码。)

第三,与类似Java的示例不同,getter无法接受其他参数。现在,我已经可以听到您说的了,好吧,如果它带有参数,那不是吸气剂!从官方的角度来看,这是正确的。但从实际意义上讲,没有理由我们不能参数化命名属性(例如)x,并为某些特定参数设置其值。

如果我们可以做类似的事情会很好:

e.x[a,b,c] = 10
e.x[d,e,f] = 20

例如。我们得到的最接近的结果是重写赋值,以暗示某些特殊的语义:

e.x = [a,b,c,10]
e.x = [d,e,f,30]

并且当然要确保我们的设置者知道如何提取前三个值作为字典的键并将其值设置为数字或其他内容。

但是即使这样做,我们仍然不能用属性来支持它,因为没有办法获取值,因为我们根本无法将参数传递给getter。因此,我们必须返回所有内容,并引入了不对称性。

Java风格的getter / setter确实可以解决这个问题,但我们又回到了需要getter / setter的地方。

在我看来,我们真正想要的是满足以下要求的东西:

  • 用户仅为给定属性定义一种方法,并可以在其中指示该属性是只读还是读写属性。如果属性为可写属性,则此测试将失败。

  • 用户无需在函数下面定义额外的变量,因此我们不需要代码中的__init__setattr。实际上,由于我们已经创建了这种新样式属性,因此该变量存在。

  • 该属性的任何默认代码都在方法主体本身中执行。

  • 我们可以将属性设置为属性,并将其引用为属性。

  • 我们可以参数化属性。

在代码方面,我们需要一种编写方式:

def x(self, *args):
    return defaultX()

然后能够执行以下操作:

print e.x     -> The default at time T0
e.x = 1
print e.x     -> 1
e.x = None
print e.x     -> The default at time T1

等等。

我们还希望有一种方法可以针对可参数化属性的特殊情况执行此操作,但仍允许默认的大小写有效。您将在下面看到我的处理方法。

现在到了要点(是的!要点!)。我为此提出的解决方案如下。

我们创建一个新对象来替换属性的概念。该对象旨在存储为其设置的变量的值,而且还维护知道如何计算默认值的代码的句柄。它的工作是存储设置value或运行该method值(如果未设置)。

我们称它为UberProperty

class UberProperty(object):

    def __init__(self, method):
        self.method = method
        self.value = None
        self.isSet = False

    def setValue(self, value):
        self.value = value
        self.isSet = True

    def clearValue(self):
        self.value = None
        self.isSet = False

我假设method这里是一个类方法,value是的值UberProperty,并且我添加了它,isSet因为它None可能是真实值,这使我们可以采用一种干净的方式来声明确实没有“值”。另一种方式是某种形式的哨兵。

基本上,这给了我们一个可以做我们想要的对象的对象,但是实际上如何将它放在我们的类上呢?好吧,属性使用装饰器;我们为什么不能呢?让我们看看它的外观(从这里开始,我将坚持只使用一个’attribute’,x)。

class Example(object):

    @uberProperty
    def x(self):
        return defaultX()

当然,这实际上还行不通。我们必须实现uberProperty并确保它能够处理获取和设置。

让我们从获取开始。

我的第一次尝试是简单地创建一个新的UberProperty对象并返回它:

def uberProperty(f):
    return UberProperty(f)

当然,我很快发现这是行不通的:Python从不将可调用对象绑定到对象,并且我需要对象才能调用该函数。即使在类中创建装饰器也不起作用,尽管现在我们有了类,但仍然没有可以使用的对象。

因此,我们将需要在这里做更多的事情。我们确实知道一种方法只需要表示一次,所以让我们继续保留装饰器,但是修改UberProperty为仅存储method引用:

class UberProperty(object):

    def __init__(self, method):
        self.method = method

它也是不可调用的,因此目前没有任何效果。

我们如何完成图片?好吧,当我们使用新的装饰器创建示例类时,最终会得到什么:

class Example(object):

    @uberProperty
    def x(self):
        return defaultX()

print Example.x     <__main__.UberProperty object at 0x10e1fb8d0>
print Example().x   <__main__.UberProperty object at 0x10e1fb8d0>

在这两种情况下,我们都返回UberProperty哪个当然不是可调用的,所以这没什么用。

我们需要的是一种UberProperty在类创建后将装饰者创建的实例动态绑定到该类的对象,然后将该对象返回给该用户使用的动态绑定方法。嗯,是的__init__,老兄。

让我们写下我们希望我们的搜索结果为第一的内容。我们将an绑定UberProperty到实例,因此要返回的显而易见的东西是BoundUberProperty。这是我们实际维护x属性状态的地方。

class BoundUberProperty(object):
    def __init__(self, obj, uberProperty):
        self.obj = obj
        self.uberProperty = uberProperty
        self.isSet = False

    def setValue(self, value):
        self.value = value
        self.isSet = True

    def getValue(self):
        return self.value if self.isSet else self.uberProperty.method(self.obj)

    def clearValue(self):
        del self.value
        self.isSet = False

现在我们来表示;如何将它们放在物体上?有几种方法,但是最容易解释的__init__方法就是使用该方法进行映射。到__init__我们的装饰器运行时为止,所以只需要浏览对象的对象__dict__并更新属性值是type的所有属性UberProperty

现在,uber-properties很酷,我们可能会想大量使用它们,因此仅创建一个对所有子类都执行此操作的基类是有意义的。我认为您知道将要调用的基类。

class UberObject(object):
    def __init__(self):
        for k in dir(self):
            v = getattr(self, k)
            if isinstance(v, UberProperty):
                v = BoundUberProperty(self, v)
                setattr(self, k, v)

我们添加此代码,将示例更改为从继承UberObject,并…

e = Example()
print e.x               -> <__main__.BoundUberProperty object at 0x104604c90>

修改x为:

@uberProperty
def x(self):
    return *datetime.datetime.now()*

我们可以运行一个简单的测试:

print e.x.getValue()
print e.x.getValue()
e.x.setValue(datetime.date(2013, 5, 31))
print e.x.getValue()
e.x.clearValue()
print e.x.getValue()

然后我们得到想要的输出:

2013-05-31 00:05:13.985813
2013-05-31 00:05:13.986290
2013-05-31
2013-05-31 00:05:13.986310

(老兄,我迟到了。)

请注意,我已经使用getValuesetValue以及clearValue在这里。这是因为我还没有链接自动返回这些值的方法。

但是我认为这是一个停止的好地方,因为我累了。您还可以看到我们所需的核心功能已经到位。其余的是橱窗装饰。重要的可用性窗口修饰,但是可以等到我进行更改以更新帖子。

我将通过解决以下问题来完成下一个示例中的示例:

  • 我们需要确保UberObject __init__始终由子类调用。

    • 因此,我们要么强制在某个地方调用它,要么阻止其实现。
    • 我们将看到如何使用元类来做到这一点。
  • 我们需要确保能够处理某些人将函数“别名”为其他事物的常见情况,例如:

      class Example(object):
          @uberProperty
          def x(self):
              ...
    
          y = x
  • 我们需要默认e.x返回e.x.getValue()

    • 我们实际上将看到的是模型失败的领域。
    • 事实证明,我们始终需要使用函数调用来获取值。
    • 但是我们可以使其看起来像常规函数调用,而不必使用e.x.getValue()。(如果您还没有解决问题,那么这样做很明显。)
  • 我们需要支持设置e.x directly,如中所示e.x = <newvalue>。我们也可以在父类中执行此操作,但是我们需要更新__init__代码以进行处理。

  • 最后,我们将添加参数化属性。我们也将如何做到这一点很明显。

这是到目前为止的代码:

import datetime

class UberObject(object):
    def uberSetter(self, value):
        print 'setting'

    def uberGetter(self):
        return self

    def __init__(self):
        for k in dir(self):
            v = getattr(self, k)
            if isinstance(v, UberProperty):
                v = BoundUberProperty(self, v)
                setattr(self, k, v)


class UberProperty(object):
    def __init__(self, method):
        self.method = method

class BoundUberProperty(object):
    def __init__(self, obj, uberProperty):
        self.obj = obj
        self.uberProperty = uberProperty
        self.isSet = False

    def setValue(self, value):
        self.value = value
        self.isSet = True

    def getValue(self):
        return self.value if self.isSet else self.uberProperty.method(self.obj)

    def clearValue(self):
        del self.value
        self.isSet = False

    def uberProperty(f):
        return UberProperty(f)

class Example(UberObject):

    @uberProperty
    def x(self):
        return datetime.datetime.now()

[1]对于是否仍然如此,我可能会落后。

[TL;DR? You can skip to the end for a code example.]

I actually prefer to use a different idiom, which is a little involved for using as a one off, but is nice if you have a more complex use case.

A bit of background first.

Properties are useful in that they allow us to handle both setting and getting values in a programmatic way but still allow attributes to be accessed as attributes. We can turn ‘gets’ into ‘computations’ (essentially) and we can turn ‘sets’ into ‘events’. So let’s say we have the following class, which I’ve coded with Java-like getters and setters.

class Example(object):
    def __init__(self, x=None, y=None):
        self.x = x
        self.y = y

    def getX(self):
        return self.x or self.defaultX()

    def getY(self):
        return self.y or self.defaultY()

    def setX(self, x):
        self.x = x

    def setY(self, y):
        self.y = y

    def defaultX(self):
        return someDefaultComputationForX()

    def defaultY(self):
        return someDefaultComputationForY()

You may be wondering why I didn’t call defaultX and defaultY in the object’s __init__ method. The reason is that for our case I want to assume that the someDefaultComputation methods return values that vary over time, say a timestamp, and whenever x (or y) is not set (where, for the purpose of this example, “not set” means “set to None”) I want the value of x‘s (or y‘s) default computation.

So this is lame for a number of reasons describe above. I’ll rewrite it using properties:

class Example(object):
    def __init__(self, x=None, y=None):
        self._x = x
        self._y = y

    @property
    def x(self):
        return self.x or self.defaultX()

    @x.setter
    def x(self, value):
        self._x = value

    @property
    def y(self):
        return self.y or self.defaultY()

    @y.setter
    def y(self, value):
        self._y = value

    # default{XY} as before.

What have we gained? We’ve gained the ability to refer to these attributes as attributes even though, behind the scenes, we end up running methods.

Of course the real power of properties is that we generally want these methods to do something in addition to just getting and setting values (otherwise there is no point in using properties). I did this in my getter example. We are basically running a function body to pick up a default whenever the value isn’t set. This is a very common pattern.

But what are we losing, and what can’t we do?

The main annoyance, in my view, is that if you define a getter (as we do here) you also have to define a setter.[1] That’s extra noise that clutters the code.

Another annoyance is that we still have to initialize the x and y values in __init__. (Well, of course we could add them using setattr() but that is more extra code.)

Third, unlike in the Java-like example, getters cannot accept other parameters. Now I can hear you saying already, well, if it’s taking parameters it’s not a getter! In an official sense, that is true. But in a practical sense there is no reason we shouldn’t be able to parameterize an named attribute — like x — and set its value for some specific parameters.

It’d be nice if we could do something like:

e.x[a,b,c] = 10
e.x[d,e,f] = 20

for example. The closest we can get is to override the assignment to imply some special semantics:

e.x = [a,b,c,10]
e.x = [d,e,f,30]

and of course ensure that our setter knows how to extract the first three values as a key to a dictionary and set its value to a number or something.

But even if we did that we still couldn’t support it with properties because there is no way to get the value because we can’t pass parameters at all to the getter. So we’ve had to return everything, introducing an asymmetry.

The Java-style getter/setter does let us handle this, but we’re back to needing getter/setters.

In my mind what we really want is something that capture the following requirements:

  • Users define just one method for a given attribute and can indicate there whether the attribute is read-only or read-write. Properties fail this test if the attribute writable.

  • There is no need for the user to define an extra variable underlying the function, so we don’t need the __init__ or setattr in the code. The variable just exists by the fact we’ve created this new-style attribute.

  • Any default code for the attribute executes in the method body itself.

  • We can set the attribute as an attribute and reference it as an attribute.

  • We can parameterize the attribute.

In terms of code, we want a way to write:

def x(self, *args):
    return defaultX()

and be able to then do:

print e.x     -> The default at time T0
e.x = 1
print e.x     -> 1
e.x = None
print e.x     -> The default at time T1

and so forth.

We also want a way to do this for the special case of a parameterizable attribute, but still allow the default assign case to work. You’ll see how I tackled this below.

Now to the point (yay! the point!). The solution I came up for for this is as follows.

We create a new object to replace the notion of a property. The object is intended to store the value of a variable set to it, but also maintains a handle on code that knows how to calculate a default. Its job is to store the set value or to run the method if that value is not set.

Let’s call it an UberProperty.

class UberProperty(object):

    def __init__(self, method):
        self.method = method
        self.value = None
        self.isSet = False

    def setValue(self, value):
        self.value = value
        self.isSet = True

    def clearValue(self):
        self.value = None
        self.isSet = False

I assume method here is a class method, value is the value of the UberProperty, and I have added isSet because None may be a real value and this allows us a clean way to declare there really is “no value”. Another way is a sentinel of some sort.

This basically gives us an object that can do what we want, but how do we actually put it on our class? Well, properties use decorators; why can’t we? Let’s see how it might look (from here on I’m going to stick to using just a single ‘attribute’, x).

class Example(object):

    @uberProperty
    def x(self):
        return defaultX()

This doesn’t actually work yet, of course. We have to implement uberProperty and make sure it handles both gets and sets.

Let’s start with gets.

My first attempt was to simply create a new UberProperty object and return it:

def uberProperty(f):
    return UberProperty(f)

I quickly discovered, of course, that this doens’t work: Python never binds the callable to the object and I need the object in order to call the function. Even creating the decorator in the class doesn’t work, as although now we have the class, we still don’t have an object to work with.

So we’re going to need to be able to do more here. We do know that a method need only be represented the one time, so let’s go ahead and keep our decorator, but modify UberProperty to only store the method reference:

class UberProperty(object):

    def __init__(self, method):
        self.method = method

It is also not callable, so at the moment nothing is working.

How do we complete the picture? Well, what do we end up with when we create the example class using our new decorator:

class Example(object):

    @uberProperty
    def x(self):
        return defaultX()

print Example.x     <__main__.UberProperty object at 0x10e1fb8d0>
print Example().x   <__main__.UberProperty object at 0x10e1fb8d0>

in both cases we get back the UberProperty which of course is not a callable, so this isn’t of much use.

What we need is some way to dynamically bind the UberProperty instance created by the decorator after the class has been created to an object of the class before that object has been returned to that user for use. Um, yeah, that’s an __init__ call, dude.

Let’s write up what we want our find result to be first. We’re binding an UberProperty to an instance, so an obvious thing to return would be a BoundUberProperty. This is where we’ll actually maintain state for the x attribute.

class BoundUberProperty(object):
    def __init__(self, obj, uberProperty):
        self.obj = obj
        self.uberProperty = uberProperty
        self.isSet = False

    def setValue(self, value):
        self.value = value
        self.isSet = True

    def getValue(self):
        return self.value if self.isSet else self.uberProperty.method(self.obj)

    def clearValue(self):
        del self.value
        self.isSet = False

Now we the representation; how do get these on to an object? There are a few approaches, but the easiest one to explain just uses the __init__ method to do that mapping. By the time __init__ is called our decorators have run, so just need to look through the object’s __dict__ and update any attributes where the value of the attribute is of type UberProperty.

Now, uber-properties are cool and we’ll probably want to use them a lot, so it makes sense to just create a base class that does this for all subclasses. I think you know what the base class is going to be called.

class UberObject(object):
    def __init__(self):
        for k in dir(self):
            v = getattr(self, k)
            if isinstance(v, UberProperty):
                v = BoundUberProperty(self, v)
                setattr(self, k, v)

We add this, change our example to inherit from UberObject, and …

e = Example()
print e.x               -> <__main__.BoundUberProperty object at 0x104604c90>

After modifying x to be:

@uberProperty
def x(self):
    return *datetime.datetime.now()*

We can run a simple test:

print e.x.getValue()
print e.x.getValue()
e.x.setValue(datetime.date(2013, 5, 31))
print e.x.getValue()
e.x.clearValue()
print e.x.getValue()

And we get the output we wanted:

2013-05-31 00:05:13.985813
2013-05-31 00:05:13.986290
2013-05-31
2013-05-31 00:05:13.986310

(Gee, I’m working late.)

Note that I have used getValue, setValue, and clearValue here. This is because I haven’t yet linked in the means to have these automatically returned.

But I think this is a good place to stop for now, because I’m getting tired. You can also see that the core functionality we wanted is in place; the rest is window dressing. Important usability window dressing, but that can wait until I have a change to update the post.

I’ll finish up the example in the next posting by addressing these things:

  • We need to make sure UberObject’s __init__ is always called by subclasses.

    • So we either force it be called somewhere or we prevent it from being implemented.
    • We’ll see how to do this with a metaclass.
  • We need to make sure we handle the common case where someone ‘aliases’ a function to something else, such as:

      class Example(object):
          @uberProperty
          def x(self):
              ...
    
          y = x
    
  • We need e.x to return e.x.getValue() by default.

    • What we’ll actually see is this is one area where the model fails.
    • It turns out we’ll always need to use a function call to get the value.
    • But we can make it look like a regular function call and avoid having to use e.x.getValue(). (Doing this one is obvious, if you haven’t already fixed it out.)
  • We need to support setting e.x directly, as in e.x = <newvalue>. We can do this in the parent class too, but we’ll need to update our __init__ code to handle it.

  • Finally, we’ll add parameterized attributes. It should be pretty obvious how we’ll do this, too.

Here’s the code as it exists up to now:

import datetime

class UberObject(object):
    def uberSetter(self, value):
        print 'setting'

    def uberGetter(self):
        return self

    def __init__(self):
        for k in dir(self):
            v = getattr(self, k)
            if isinstance(v, UberProperty):
                v = BoundUberProperty(self, v)
                setattr(self, k, v)


class UberProperty(object):
    def __init__(self, method):
        self.method = method

class BoundUberProperty(object):
    def __init__(self, obj, uberProperty):
        self.obj = obj
        self.uberProperty = uberProperty
        self.isSet = False

    def setValue(self, value):
        self.value = value
        self.isSet = True

    def getValue(self):
        return self.value if self.isSet else self.uberProperty.method(self.obj)

    def clearValue(self):
        del self.value
        self.isSet = False

    def uberProperty(f):
        return UberProperty(f)

class Example(UberObject):

    @uberProperty
    def x(self):
        return datetime.datetime.now()

[1] I may be behind on whether this is still the case.


回答 5

我认为两者都有自己的位置。使用的一个问题@property是,很难使用标准的类机制来扩展子类中的getter或setter的行为。问题在于实际的获取器/设置器函数隐藏在属性中。

您实际上可以掌握这些功能,例如

class C(object):
    _p = 1
    @property
    def p(self):
        return self._p
    @p.setter
    def p(self, val):
        self._p = val

您可以访问getter和setter功能C.p.fgetC.p.fset,但你不能轻易使用正常方法继承(如超)设备来扩展他们。在深入研究了super的复杂性之后,您确实可以通过以下方式使用super:

# Using super():
class D(C):
    # Cannot use super(D,D) here to define the property
    # since D is not yet defined in this scope.
    @property
    def p(self):
        return super(D,D).p.fget(self)

    @p.setter
    def p(self, val):
        print 'Implement extra functionality here for D'
        super(D,D).p.fset(self, val)

# Using a direct reference to C
class E(C):
    p = C.p

    @p.setter
    def p(self, val):
        print 'Implement extra functionality here for E'
        C.p.fset(self, val)

但是,使用super()非常麻烦,因为必须重新定义该属性,并且您必须使用略有反直觉的super(cls,cls)机制来获取p的未绑定副本。

I think both have their place. One issue with using @property is that it is hard to extend the behaviour of getters or setters in subclasses using standard class mechanisms. The problem is that the actual getter/setter functions are hidden in the property.

You can actually get hold of the functions, e.g. with

class C(object):
    _p = 1
    @property
    def p(self):
        return self._p
    @p.setter
    def p(self, val):
        self._p = val

you can access the getter and setter functions as C.p.fget and C.p.fset, but you can’t easily use the normal method inheritance (e.g. super) facilities to extend them. After some digging into the intricacies of super, you can indeed use super in this way:

# Using super():
class D(C):
    # Cannot use super(D,D) here to define the property
    # since D is not yet defined in this scope.
    @property
    def p(self):
        return super(D,D).p.fget(self)

    @p.setter
    def p(self, val):
        print 'Implement extra functionality here for D'
        super(D,D).p.fset(self, val)

# Using a direct reference to C
class E(C):
    p = C.p

    @p.setter
    def p(self, val):
        print 'Implement extra functionality here for E'
        C.p.fset(self, val)

Using super() is, however, quite clunky, since the property has to be redefined, and you have to use the slightly counter-intuitive super(cls,cls) mechanism to get an unbound copy of p.


回答 6

对我来说,使用属性更直观,并且更适合大多数代码。

比较中

o.x = 5
ox = o.x

o.setX(5)
ox = o.getX()

在我看来,这很容易阅读。属性也使私有变量变得更加容易。

Using properties is to me more intuitive and fits better into most code.

Comparing

o.x = 5
ox = o.x

vs.

o.setX(5)
ox = o.getX()

is to me quite obvious which is easier to read. Also properties allows for private variables much easier.


回答 7

在大多数情况下,我都不想使用两者。属性的问题在于它们使类不那么透明。特别是,如果您要向设置员提出exceptions情况,这将成为一个问题。例如,如果您具有Account.email属性:

class Account(object):
    @property
    def email(self):
        return self._email

    @email.setter
    def email(self, value):
        if '@' not in value:
            raise ValueError('Invalid email address.')
        self._email = value

那么该类的用户就不会期望为该属性分配值会导致异常:

a = Account()
a.email = 'badaddress'
--> ValueError: Invalid email address.

结果,异常可能无法处理,或者在调用链中传播得太高而无法正确处理,或者导致向程序用户呈现非常无用的回溯(在python和java的世界中,这实在太普遍了)。

我也避免使用getter和setter:

  • 因为预先为所有属性定义它们非常耗时,
  • 不必要地增加了代码量,使理解和维护代码更加困难,
  • 如果仅根据需要为属性定义它们,则类的界面将发生变化,从而损害该类的所有用户

我更喜欢在定义明确的位置(例如在验证方法中)执行复杂的逻辑,而不是使用属性和获取/设置方法:

class Account(object):
    ...
    def validate(self):
        if '@' not in self.email:
            raise ValueError('Invalid email address.')

或类似的Account.save方法。

请注意,我并不是想说在任何情况下属性都是有用的,只是如果您可以使类足够简单和透明以至于不需要它们,则可能会更好。

I would prefer to use neither in most cases. The problem with properties is that they make the class less transparent. Especially, this is an issue if you were to raise an exception from a setter. For example, if you have an Account.email property:

class Account(object):
    @property
    def email(self):
        return self._email

    @email.setter
    def email(self, value):
        if '@' not in value:
            raise ValueError('Invalid email address.')
        self._email = value

then the user of the class does not expect that assigning a value to the property could cause an exception:

a = Account()
a.email = 'badaddress'
--> ValueError: Invalid email address.

As a result, the exception may go unhandled, and either propagate too high in the call chain to be handled properly, or result in a very unhelpful traceback being presented to the program user (which is sadly too common in the world of python and java).

I would also avoid using getters and setters:

  • because defining them for all properties in advance is very time consuming,
  • makes the amount of code unnecessarily longer, which makes understanding and maintaining the code more difficult,
  • if you were define them for properties only as needed, the interface of the class would change, hurting all users of the class

Instead of properties and getters/setters I prefer doing the complex logic in well defined places such as in a validation method:

class Account(object):
    ...
    def validate(self):
        if '@' not in self.email:
            raise ValueError('Invalid email address.')

or a similiar Account.save method.

Note that I am not trying to say that there are no cases when properties are useful, only that you may be better off if you can make your classes simple and transparent enough that you don’t need them.


回答 8

我觉得属性是关于让您仅在实际需要时才编写getter和setter的开销。

Java编程文化强烈建议永远不要访问属性,而应通过getter和setter以及仅实际需要的属性进行访问。总是编写这些显而易见的代码片段有点冗长,请注意,有70%的时间从未将它们替换为一些非平凡的逻辑。

在Python中,人们实际上关心这种开销,因此您可以采用以下做法:

  • 如果不需要,首先不要使用getter和setter。
  • 使用@property予以实施而又不改变你的代码的其余部分的语法。

I feel like properties are about letting you get the overhead of writing getters and setters only when you actually need them.

Java Programming culture strongly advise to never give access to properties, and instead, go through getters and setters, and only those which are actually needed. It’s a bit verbose to always write these obvious pieces of code, and notice that 70% of the time they are never replaced by some non-trivial logic.

In Python, people actually care for that kind of overhead, so that you can embrace the following practice :

  • Do not use getters and setters at first, when if they not needed
  • Use @property to implement them without changing the syntax of the rest of your code.

回答 9

令我惊讶的是,没有人提到属性是描述符类的绑定方法,Adam DonohueNeilenMarais在他们的帖子中确切地了解了这个想法-getter和setter是函数,可以用来:

  • 验证
  • 修改数据
  • 鸭子类型(强制类型为其他类型)

这提供了一种隐藏实现细节和代码残废(例如正则表达式,类型强制转换,尝试..除了块,断言或计算值之外)的聪明方法。

通常,对一个对象执行CRUD通常可能很平凡,但请考虑将数据保存到关系数据库的示例。ORM可以在绑定到属性类中定义的fget,fset,fdel的方法中隐藏特定SQL语言的实现细节,该类将管理糟糕的OO代码中的.. elif .. else阶梯,从而暴露出简单易懂的优雅,self.variable = something并避免使用 ORM 为开发人员提供细节。

如果仅将属性视为束缚和纪律语言(即Java)的沉闷痕迹,那么他们就错过了描述符的要点。

I am surprised that nobody has mentioned that properties are bound methods of a descriptor class, Adam Donohue and NeilenMarais get at exactly this idea in their posts — that getters and setters are functions and can be used to:

  • validate
  • alter data
  • duck type (coerce type to another type)

This presents a smart way to hide implementation details and code cruft like regular expression, type casts, try .. except blocks, assertions or computed values.

In general doing CRUD on an object may often be fairly mundane but consider the example of data that will be persisted to a relational database. ORM’s can hide implementation details of particular SQL vernaculars in the methods bound to fget, fset, fdel defined in a property class that will manage the awful if .. elif .. else ladders that are so ugly in OO code — exposing the simple and elegant self.variable = something and obviate the details for the developer using the ORM.

If one thinks of properties only as some dreary vestige of a Bondage and Discipline language (i.e. Java) they are missing the point of descriptors.


回答 10

在复杂的项目中,我更喜欢使用带有显式setter函数的只读属性(或getter):

class MyClass(object):
...        
@property
def my_attr(self):
    ...

def set_my_attr(self, value):
    ...

在寿命长的项目中,调试和重构比编写代码本身要花费更多的时间。使用它有几个缺点@property.setter,使调试更加困难:

1)python允许为现有对象创建新属性。这使得很难跟踪以下印刷错误:

my_object.my_atttr = 4.

如果您的对象是一个复杂的算法,那么您将花费相当多的时间尝试找出为什么它不收敛(请注意,在上面的行中有一个额外的“ t”)

2)setter有时可能会演变为复杂而缓慢的方法(例如,访问数据库)。对于另一个开发人员来说,很难弄清楚为什么以下功能非常慢。他可能在分析do_something()方法上花费了大量时间,而my_object.my_attr = 4.实际上是导致速度下降的原因:

def slow_function(my_object):
    my_object.my_attr = 4.
    my_object.do_something()

In complex projects I prefer using read-only properties (or getters) with explicit setter function:

class MyClass(object):
...        
@property
def my_attr(self):
    ...

def set_my_attr(self, value):
    ...

In long living projects debugging and refactoring takes more time than writing the code itself. There are several downsides for using @property.setter that makes debugging even harder:

1) python allows creating new attributes for an existing object. This makes a following misprint very hard to track:

my_object.my_atttr = 4.

If your object is a complicated algorithm then you will spend quite some time trying to find out why it doesn’t converge (notice an extra ‘t’ in the line above)

2) setter sometimes might evolve to a complicated and slow method (e.g. hitting a database). It would be quite hard for another developer to figure out why the following function is very slow. He might spend a lot of time on profiling do_something() method, while my_object.my_attr = 4. is actually the cause of slowdown:

def slow_function(my_object):
    my_object.my_attr = 4.
    my_object.do_something()

回答 11

无论@property与传统的getter和setter方法各有优点。这取决于您的用例。

优点 @property

  • 您无需在更改数据访问的实现时更改接口。当您的项目较小时,您可能希望使用直接属性访问来访问类成员。例如,假设您有一个foo类型为object的对象Foo,该对象具有一个member num。然后,您只需使用即可获得此成员num = foo.num。随着项目的发展,您可能会觉得需要对简单的属性访问进行一些检查或调试。然后,您可以@property 使用。数据访问接口保持不变,因此无需修改客户端代码。

    引用自PEP-8

    对于简单的公共数据属性,最好仅公开属性名称,而不使用复杂的访问器/更改器方法。请记住,如果您发现简单的数据属性需要增强功能行为,则Python为将来的增强提供了简便的方法。在这种情况下,使用属性将功能实现隐藏在简单的数据属性访问语法之后。

  • 使用@property在Python中的数据访问被认为是Python的

    • 它可以增强您作为Python(不是Java)程序员的自我认同。

    • 如果您的面试官认为Java风格的getter和setter是反模式的,那么它可以帮助您进行工作面试。

传统吸气剂和吸气剂的优点

  • 与简单的属性访问相比,传统的getter和setter允许更复杂的数据访问。例如,当您设置一个类成员时,有时您需要一个标志来指示您希望在哪里强制执行此操作,即使某些情况看起来并不完美。虽然如何扩展直接成员访问权限(如)并不明显foo.num = num,但您可以通过附加force参数轻松扩展传统的setter :

    def Foo:
        def set_num(self, num, force=False):
            ...
  • 传统的getter和setter 明确表明,类成员访问是通过方法进行的。这表示:

    • 结果所得到的结果可能与该类中确切存储的结果不同。

    • 即使访问看起来像简单的属性访问,其性能也可能相差很大。

    除非您的Class用户希望@property在每个属性访问语句后都隐藏起来,否则将其明确表示可以最大程度地减少您的Class用户的意外情况。

  • @NeilenMarais本文所提到的,在子类中扩展传统的getter和setters比扩展属性更容易。

  • 长期以来,传统的吸气剂和吸气剂已以多种语言广泛使用。如果您的团队中有来自不同背景的人员,那么他们看起来比熟悉@property。另外,随着项目的发展,如果您可能需要从Python迁移到另一种不具备的语言,则@property使用传统的getter和setter可以使迁移过程更加顺畅。

注意事项

  • @property即使您使用传统的getter和setter 都不将类成员设为私有,即使您在其名称前使用双下划线也是如此:

    class Foo:
        def __init__(self):
            self.__num = 0
    
        @property
        def num(self):
            return self.__num
    
        @num.setter
        def num(self, num):
            self.__num = num
    
        def get_num(self):
            return self.__num
    
        def set_num(self, num):
            self.__num = num
    
    foo = Foo()
    print(foo.num)          # output: 0
    print(foo.get_num())    # output: 0
    print(foo._Foo__num)    # output: 0

Both @property and traditional getters and setters have their advantages. It depends on your use case.

Advantages of @property

  • You don’t have to change the interface while changing the implementation of data access. When your project is small, you probably want to use direct attribute access to access a class member. For example, let’s say you have an object foo of type Foo, which has a member num. Then you can simply get this member with num = foo.num. As your project grows, you may feel like there needs to be some checks or debugs on the simple attribute access. Then you can do that with a @property within the class. The data access interface remains the same so that there is no need to modify client code.

    Cited from PEP-8:

    For simple public data attributes, it is best to expose just the attribute name, without complicated accessor/mutator methods. Keep in mind that Python provides an easy path to future enhancement, should you find that a simple data attribute needs to grow functional behavior. In that case, use properties to hide functional implementation behind simple data attribute access syntax.

  • Using @property for data access in Python is regarded as Pythonic:

    • It can strengthen your self-identification as a Python (not Java) programmer.

    • It can help your job interview if your interviewer thinks Java-style getters and setters are anti-patterns.

Advantages of traditional getters and setters

  • Traditional getters and setters allow for more complicated data access than simple attribute access. For example, when you are setting a class member, sometimes you need a flag indicating where you would like to force this operation even if something doesn’t look perfect. While it is not obvious how to augment a direct member access like foo.num = num, You can easily augment your traditional setter with an additional force parameter:

    def Foo:
        def set_num(self, num, force=False):
            ...
    
  • Traditional getters and setters make it explicit that a class member access is through a method. This means:

    • What you get as the result may not be the same as what is exactly stored within that class.

    • Even if the access looks like a simple attribute access, the performance can vary greatly from that.

    Unless your class users expect a @property hiding behind every attribute access statement, making such things explicit can help minimize your class users surprises.

  • As mentioned by @NeilenMarais and in this post, extending traditional getters and setters in subclasses is easier than extending properties.

  • Traditional getters and setters have been widely used for a long time in different languages. If you have people from different backgrounds in your team, they look more familiar than @property. Also, as your project grows, if you may need to migrate from Python to another language that doesn’t have @property, using traditional getters and setters would make the migration smoother.

Caveats

  • Neither @property nor traditional getters and setters makes the class member private, even if you use double underscore before its name:

    class Foo:
        def __init__(self):
            self.__num = 0
    
        @property
        def num(self):
            return self.__num
    
        @num.setter
        def num(self, num):
            self.__num = num
    
        def get_num(self):
            return self.__num
    
        def set_num(self, num):
            self.__num = num
    
    foo = Foo()
    print(foo.num)          # output: 0
    print(foo.get_num())    # output: 0
    print(foo._Foo__num)    # output: 0
    

回答 12

这是“有效的Python:编写更好的Python的90种特定方法”的摘录(很棒的书。我强烈推荐它)。

要记住的事情

using使用简单的公共属性定义新的类接口,并避免定义setter和getter方法。

necessary必要时,使用@property定义在对象上访问属性时的特殊行为。

@在您的@property方法中遵循最小惊喜规则,并避免出现奇怪的副作用。

✦确保@property方法是快速的;对于缓慢或复杂的工作(尤其是涉及I / O或引起副作用的工作),请改用常规方法。

@property的一种高级但通用的用法是将曾经简单的数字属性转换为即时计算。这非常有用,因为它使您可以将类的所有现有用法迁移到新行为,而无需重写任何调用站点(如果您无法控制调用代码,这尤其重要)。@property还提供了一个重要的权宜之计,用于随着时间的推移改进接口。

我特别喜欢@property,因为它可以让您随着时间的推移逐步向更好的数据模型发展。
@property是一个工具,可帮助您解决在实际代码中遇到的问题。不要过度使用它。当您发现自己反复扩展@property方法时,可能是时候重构您的类,而不是进一步讨论代码的不良设计了。

✦使用@property为现有实例属性赋予新功能。

using通过使用@property,逐步朝着更好的数据模型发展。

find当您过多地使用@property时,请考虑重构一个类和所有调用站点。

Here is an excerpts from “Effective Python: 90 Specific Ways to Write Better Python” (Amazing book. I highly recommend it).

Things to Remember

✦ Define new class interfaces using simple public attributes and avoid defining setter and getter methods.

✦ Use @property to define special behavior when attributes are accessed on your objects, if necessary.

✦ Follow the rule of least surprise and avoid odd side effects in your @property methods.

✦ Ensure that @property methods are fast; for slow or complex work—especially involving I/O or causing side effects—use normal methods instead.

One advanced but common use of @property is transitioning what was once a simple numerical attribute into an on-the-fly calculation. This is extremely helpful because it lets you migrate all existing usage of a class to have new behaviors without requiring any of the call sites to be rewritten (which is especially important if there’s calling code that you don’t control). @property also provides an important stopgap for improving interfaces over time.

I especially like @property because it lets you make incremental progress toward a better data model over time.
@property is a tool to help you address problems you’ll come across in real-world code. Don’t overuse it. When you find yourself repeatedly extending @property methods, it’s probably time to refactor your class instead of further paving over your code’s poor design.

✦ Use @property to give existing instance attributes new functionality.

✦ Make incremental progress toward better data models by using @property.

✦ Consider refactoring a class and all call sites when you find yourself using @property too heavily.