存在多种基于初始属性在init方法之外创建对象属性的方法 [英] DIfferent ways exist to create object attributes outside of init method based on the initial attributes

查看:48
本文介绍了存在多种基于初始属性在init方法之外创建对象属性的方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试理解OOP(特别是在Python 3中)。这是一个基本的类模板:

 类行:

参数:
的坐标列表。

def __init __(self,points):
self.x1 = points [0]
self.y1 = points [1]
self.x2 = points [ 2]
self.y2 =点[3]

我通过开始和结束(x ,y)协调到该类的列表中。
但是,我还要向该类对象添加 length slope y-intercept 属性c $ c>行(注意:我 要它们作为方法)。这是我发现的几种方法-


添加到init方法本身


  class Lines :

def __init __(self,points):
self.x1 = points [0]
self.y1 = points [1]
self.x2 =点[2]
self.y2 =点[3]

##创建长度,斜率和y_intercept属性

self.length = round(((( .x2-self.x1)** 2 +(self.y2-self.y1)** 2)** 0.5,2)
self.slope = round((self.y2-self.y1)/ (self.x2-self.x1),1)
self.y_intercept = self.y1-self.slope * self.x1


为其创建方法并使用@property装饰器


  class行:

def __init __(self,points):
self.x1 = points [0]
self.y1 = points [1]
self.x2 = points [2]
self .y2 = points [3]

@property
def length(self):
return round(((self .x2-self.x1)** 2 +(self.y2-self.y1)** 2)** 0.5,2)

@property
deflope(self):
return round((self.y2-self.y1)/(self.x2-self.x1),1)

@property
def y_intercept(self):
return self.y1-self.slope * self.x1


我的问题:


我不喜欢第一种方法(使用 init()),因为它看起来有点笨拙,无法容纳像这样的基于计算的代码在那里。我的理解是,init方法仅用于初始化对象的属性,而不仅限于此。


我可以使用属性装饰器,然后访问对象的坡度等。像这样

  line1 = Lines([0,0,2,4])
line1.slope

但是,当我打印 line1 .__ dict __ 时,它们未在可用属性中列出。 / p>

我的问题


我真正想知道的是是否还有其他(更常用的Pythonic方式)将属性设置为对象(例如:坡度)基于初始属性(例如:x1,y1)。我认为这将是一个非常普遍的问题(例如,将一组基本属性作为输入,然后基于它们将其他更高级的属性设置为同一对象),但是我在这里没有发现太多。我确定这里缺少一些简单而优雅的解决方案。


谢谢。我希望我对我的问题很清楚。

解决方案

您可以通过几种方式来做到这一点,也许它们并不常见 ;,但它们仍然是 Pythonic


首先, __ dict __


考虑以下简单代码:

  class行:
pass

line1 = Lines()

现在,我想更改属性 xy ,我的意思是将其设置为 10 。一旦Python是动态的并且Python中的所有对象都是对象,我就可以做到:

  class行:
pass

line1 = Lines()

line1.xy = 10
print(f''line1.xy = {line1.xy})#输出:line1.xy = 10

什么?如何设置不存在的属性? __ dict __ 很简单。它存储通过实例设置的每个属性。

  class行:
传递

line1 =行()

line1.xy = 10
print(f xy = {line1.xy})#输出:xy = 10

print(f ; __ dict__ = {line1 .__ dict__}))输出:__dict__ = {'xy':10}

如果 __ dict __ 存储通过实例设置的属性,那么在设置任何属性之前, __ dict __ 状态如何?可以推断,它开始为空。


因此,您应该记住 __ dict __ 不会显示可用的属性,但是设置的属性,并且这些属性是否在主类


但是,当我打印 line1 .__ dict __ 时,它们未在可用属性中列出。


实际上, __ dict __ 中的所有属性都可以通过它使用,但是如果您尝试获取 __ dict __ ,但在类中已定义您可以获取它。怎么样?看一下:

 类行:
x1 = 3
x2 = 7

line1 = Lines()

print(f __ dict__ = {line1 .__ dict__})#输出:__dict__ = {}

print(f x1 = {line1。 x1})#输出:x1 = 3
print(f x2 = {line1.x2})#输出:x2 = 7

print(f __ dict__ = { line1 .__ dict__})#输出:__dict__ = {}

line1.x1 = 9#设置x1,因此它将存储在__dict__
print(f __ dict__ = {line1 .__ dict__})#输出:__dict__ = {'x1':9}

print(f x1 = {line1.x1})#输出:x1 = 9
print(f x2 = {line1.x2})#输出:x2 = 7

print(f test = {line1.test})#输出:AttributeError

python在这里做什么?! Python首先进入 __ dict __ 并尝试查找属性 x1 x2 ,因此如果找不到它们,则转到主类,如果找到它们,它将为您返回它们,如果没有Python Raise AttributeError


__ dict __ 很棒,它可以使您做很多事情东西的。由于任何属性值都存储在 __ dict __ 中,因此可以直接获取它的值,请看:

  example = line1 .__ dict __ ['x1'] 
print(f __ dict __ ['x1'] = {example})#输出:__dict __ ['x1'] = 9


您的问题和问题


关于pythonic代码的争论确实很令人发指,但我们必须问自己是否这些代码是否是python,如果某些代码是python而没有即兴 ,那是什么意思,所以它是pythonic的,即使该代码不是很普通,例如方法 getattr() setattr() delattr(),有时它们的使用比点符号 更明智,因为它们更可靠地理解,尤其是当您深入研究时在做什么,你在调试。 (至少我是这样想的)


因此,一切都围绕着您正在做的事情以及您想做的事情。假设您有一个高质量的代码,并且使用的功能不是那么常见,那不是Pythonic吗?要清楚地想像,您正在编写一个每次交易都会增加的计数,因此,您使用常见的方法 count + = 1 ,您将超越并使用 itertools.count(),毫无疑问,您拥有高质量的代码,即使通常人们不使用该模块,也可以在此处查看更多信息: itertools —为高效循环创建迭代器的函数


因此让我们看一下代码:如您所见,这是解决此问题的另一种方法。

 类行:

def __init __(self,points):
self.x1 = points [0]
self.y1 = points [1]
self.x2 = points [2]
self .y2 = points [3]

self.length = self._length()
self.slope = self._slope()
self.y_intercept = self._y_intercept()

def _length(self):
return round(((s elf.x2-self.x1)** 2 +(self.y2-self.y1)** 2)** 0.5,2)

def _slope(self):
返回round((self.y2-self.y1)/(self.x2-self.x1),1)

def _y_intercept(self):
返回self.y1-self.slope * self.x1

line1 =行([0,0,2,4])
打印(line1.length)
打印(line1.slope)
print(line1.y_intercept)

使用只读属性真的很有趣,实际上它甚至还使您能够使用缓存(当然,如果我们写的话!),因此您不需要calc就可以将属性 length slope y_intercept 每次调用这些变量时,只有在更改属性


希望再见!


请勿使用即兴创作,而应使用Python。


I'm trying to understand OOP (specifically, in Python 3). Here's a basic class template:

class Lines:
    """
    Arguments: list of coordinates
    """

    def __init__(self, points):
        self.x1 = points[0]
        self.y1 = points[1]
        self.x2 = points[2]
        self.y2 = points[3]
        

I pass starting and ending (x,y) coordinates in a list to the class. However, I want to also add a length, slope, and y-intercept attribute to the objects of this class Lines (Note: I do not want them as methods). Here are a couple ways I found to do so-

Add to the init method itself

class Lines:

    def __init__(self, points):
        self.x1 = points[0]
        self.y1 = points[1]
        self.x2 = points[2]
        self.y2 = points[3]
        
        ##Create length, slope, and y_intercept attributes

        self.length = round(((self.x2-self.x1)**2 + (self.y2-self.y1)**2) ** 0.5, 2) 
        self.slope = round((self.y2 - self.y1)/(self.x2-self.x1),1)
        self.y_intercept = self.y1 - self.slope*self.x1

Create methods for them and use the @property decorator

class Lines:

    def __init__(self, points):
        self.x1 = points[0]
        self.y1 = points[1]
        self.x2 = points[2]
        self.y2 = points[3]
        
    @property    
    def length(self):
        return round(((self.x2-self.x1)**2 + (self.y2-self.y1)**2) ** 0.5, 2)

    @property        
    def slope(self):
        return round((self.y2 - self.y1)/(self.x2-self.x1),1)

    @property
    def y_intercept(self):
        return self.y1 - self.slope*self.x1

My Issues:

I don't prefer the first method (using init()) because it just looks a little cumbersome to fit calculation-based code like that in there. My understanding is that the init method is just there to initialize the attributes of the object and not for much more than that.

I can use the property decorator and then access the objects' slopes etc like so

line1 = Lines([0,0,2,4])
line1.slope

However, when I print line1.__dict__, they are not listed in the attributes available.

My Question

What I really want to know is if there other (more commonly-used, Pythonic) ways to set attributes to the objects(ex: slope) based on the initial attributes(ex: x1,y1). I thought this would be a very common problem (i.e., having a set of basic attributes as input and then setting other more advanced attributes based on them to the same object) but I've not found not much on it on here. I'm sure I'm missing some simple yet elegant solution here.

Thank you in advance. I hope I was clear with my question.

解决方案

You can do that in a couple ways, maybe they are not "common", but they are still Pythonic.

First, The __dict__

Consider this simple code:

class Lines:
   pass

line1 = Lines()

Now, I want to change the attribute xy, I mean set it as 10. Once Python is dynamic and everything in Python is an object, I can do it, look:

class Lines:
    pass

line1 = Lines()

line1.xy = 10
print(f"line1.xy = {line1.xy}") #Output: line1.xy = 10

What? How can I set an attribute that doesn't exist?! It simple, the __dict__. It stores every attribute that is setted through the instance.

class Lines:
    pass

line1 = Lines()

line1.xy = 10
print(f"xy = {line1.xy}")  #Output: xy = 10

print(f"__dict__ = {line1.__dict__}")  #Output: __dict__ = {'xy': 10}

So, if __dict__ stores attributes that was setted through the instance, how is the __dict__ state before I set any attribute?! As you can deduce, it starts empty.

So you should remember that the __dict__ doesn't show the attributes available, but the attributes that was setted, and it doesn't matter if those attributes exist or not in the main class

However, when I print line1.__dict__, they are not listed in the attributes available.

In fact all attributes in __dict__ are available through it, but if you try to get any other attribute that is not in __dict__, but was definided in the class you can get it. HOW? Take a look:

class Lines:
    x1 = 3
    x2 = 7

line1 = Lines()

print(f"__dict__ = {line1.__dict__}")  #Output: __dict__ = {}

print(f"x1 = {line1.x1}") #Output: x1 = 3
print(f"x2 = {line1.x2}") #Output: x2 = 7

print(f"__dict__ = {line1.__dict__}") #Output: __dict__ = {}

line1.x1 = 9 #Setting x1, so it will be stored at __dict__
print(f"__dict__ = {line1.__dict__}") #Output: __dict__ = {'x1': 9}

print(f"x1 = {line1.x1}") #Output: x1 = 9
print(f"x2 = {line1.x2}") #Output: x2 = 7

print(f"test = {line1.test}") #Output: AttributeError

What is python doing here?! Python first goes to __dict__ and try to find the attributes x1 and x2, so if it doesn't find them there, then it goes to the main class, if it finds them, it returns them for you, if not Python Raise an AttributeError.

__dict__ is awesome, it enables you to do lots of things. Since any attribute value is stored in __dict__ you can get its value directly by it, look:

example = line1.__dict__['x1']
print(f"__dict__['x1'] = {example}") #Output: __dict__['x1'] = 9

Your Issues and Question

The discurss about pythonic code is really insteresting, but we have to ask ourselves if those code are python or not, what I mean if some code are python and there is no "improvisation", so it's pythonic, even if that code are not so commum, for instance the methods getattr(), setattr() and delattr(), some times the use of them are more insterreing than the "dotted notation", because they are more reliable to understand, especially when you have a deep dive in what are doing and you are debugging. (At Least I think like that)

So everything goes around what you are doing and and what you want to do. Imagine you have a high quality code and you use not so common functions, isn't your code Pythonic?! To be clear imagine you are writing a count that is increased every time a transaction is did, so instead you use the common way count += 1, you go beyond and use the itertools.count(), without doubt you have a high quality code, even if generally people don't use that module, you can see more about it here: itertools — Functions creating iterators for efficient looping.

So let's go to the code: as you can see it's another way to solve this problem.

class Lines: 

    def __init__(self, points):
        self.x1 = points[0]
        self.y1 = points[1]
        self.x2 = points[2]
        self.y2 = points[3]

        self.length = self._length()
        self.slope = self._slope()
        self.y_intercept = self._y_intercept()
    
    def _length(self):
        return round(((self.x2-self.x1)**2 + (self.y2-self.y1)**2) ** 0.5, 2) 
    
    def _slope(self):
        return round((self.y2 - self.y1)/(self.x2-self.x1),1)
    
    def _y_intercept(self):
        return self.y1 - self.slope*self.x1

line1 = Lines([0,0,2,4])
print(line1.length)
print(line1.slope)
print(line1.y_intercept)

Your solution using Read-Only property are really interesting, actually it even enables to you work with cache too (if we write, of course!), so you don't need calc you attributes length, slope and y_intercept every time you call those variables, you can calc them values only when you change the value of the attribute points.

I hope help you, bye!

Don't use "improvisation", use Python.

这篇关于存在多种基于初始属性在init方法之外创建对象属性的方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆