Dynamically define attributes that depend on earlier defined attributes










1















I want an object that represents a path root and an arbitrary number of subdirectories that are constructed with os.path.join(root). I want to access these paths with the form self.root, self.path_a, self.path_b, etc... In addition to accessing them directly via self.path_a, I want to be able to iterate over them. Unfortunately, the approach below does not allow iterating over them via attr.astuple(paths)



The first bit of code below is what I came up with. It works but feels a little hacky to me. Since this is my first use of attrs I am wondering if there is a more intuitive/idiomatic way to approach this. It took me quite a while to figure out how to write the fairly simple class below, so I thought I might be missing something obvious.



My approach



@attr.s
class Paths(object):
subdirs = attr.ib()
root = attr.ib(default=os.getcwd())
def __attrs_post_init__(self):
for name in self.subdirs:
subdir = os.path.join(self.root, name)
object.__setattr__(self, name, subdir)

def mkdirs(self):
"""Create `root` and `subdirs` if they don't already exist."""
if not os.path.isdir(self.root):
os.mkdir(self.root)
for subdir in self.subdirs:
path = self.__getattribute__(subdir)
if not os.path.isdir(path):
os.mkdir(path)


Output



>>> p = Paths(subdirs=['a', 'b', 'c'], root='/tmp')
>>> p
Paths(subdirs=['a', 'b', 'c'], root='/tmp')
>>> p.a
'/tmp/a'
>>> p.b
'/tmp/b'
>>> p.c
'/tmp/c'


The following was my first attempt, which doesn't work.



Failed attempt



@attr.s
class Paths(object):
root = attr.ib(default=os.getcwd())
subdir_1= attr.ib(os.path.join(root, 'a'))
subdir_2= attr.ib(os.path.join(root, 'b'))


Output



------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-31-71f19d55e4c3> in <module>()
1 @attr.s
----> 2 class Paths(object):
3 root = attr.ib(default=os.getcwd())
4 subdir_1= attr.ib(os.path.join(root, 'a'))
5 subdir_2= attr.ib(os.path.join(root, 'b'))

<ipython-input-31-71f19d55e4c3> in Paths()
2 class Paths(object):
3 root = attr.ib(default=os.getcwd())
--> 4 subdir_1= attr.ib(os.path.join(root, 'a'))
5 subdir_2= attr.ib(os.path.join(root, 'b'))
6

~/miniconda3/lib/python3.6/posixpath.py in join(a, *p)
76 will be discarded. An empty last part will result in a path that
77 ends with a separator."""
--> 78 a = os.fspath(a)
79 sep = _get_sep(a)
80 path = a

TypeError: expected str, bytes or os.PathLike object, not _CountingAttr









share|improve this question
























  • This is not really what attrs is for. What exactly are you trying to accomplish?

    – roeen30
    Nov 14 '18 at 22:16











  • @roeen30 I made significant changes to the code examples, which I hope more clearly illustrates the problem I was trying to solve.

    – truthling
    Nov 15 '18 at 22:47











  • It is clearer what you're trying to do, but not why. Idiomatic path operations are provided by the excellent pathlib - take a look at it if you haven't already. As the main goal of attrs is creating quick record classes, it doesn't really help you here, where you have a class with one pre-determined attribute (root). Looks like trying to fit a square peg through a round hole.

    – roeen30
    Nov 16 '18 at 19:09







  • 1





    As a side note, if you can't avoid using __get/setattribute__(), it is clearer to just use the getattr() and setattr() builtins.

    – roeen30
    Nov 16 '18 at 19:11
















1















I want an object that represents a path root and an arbitrary number of subdirectories that are constructed with os.path.join(root). I want to access these paths with the form self.root, self.path_a, self.path_b, etc... In addition to accessing them directly via self.path_a, I want to be able to iterate over them. Unfortunately, the approach below does not allow iterating over them via attr.astuple(paths)



The first bit of code below is what I came up with. It works but feels a little hacky to me. Since this is my first use of attrs I am wondering if there is a more intuitive/idiomatic way to approach this. It took me quite a while to figure out how to write the fairly simple class below, so I thought I might be missing something obvious.



My approach



@attr.s
class Paths(object):
subdirs = attr.ib()
root = attr.ib(default=os.getcwd())
def __attrs_post_init__(self):
for name in self.subdirs:
subdir = os.path.join(self.root, name)
object.__setattr__(self, name, subdir)

def mkdirs(self):
"""Create `root` and `subdirs` if they don't already exist."""
if not os.path.isdir(self.root):
os.mkdir(self.root)
for subdir in self.subdirs:
path = self.__getattribute__(subdir)
if not os.path.isdir(path):
os.mkdir(path)


Output



>>> p = Paths(subdirs=['a', 'b', 'c'], root='/tmp')
>>> p
Paths(subdirs=['a', 'b', 'c'], root='/tmp')
>>> p.a
'/tmp/a'
>>> p.b
'/tmp/b'
>>> p.c
'/tmp/c'


The following was my first attempt, which doesn't work.



Failed attempt



@attr.s
class Paths(object):
root = attr.ib(default=os.getcwd())
subdir_1= attr.ib(os.path.join(root, 'a'))
subdir_2= attr.ib(os.path.join(root, 'b'))


Output



------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-31-71f19d55e4c3> in <module>()
1 @attr.s
----> 2 class Paths(object):
3 root = attr.ib(default=os.getcwd())
4 subdir_1= attr.ib(os.path.join(root, 'a'))
5 subdir_2= attr.ib(os.path.join(root, 'b'))

<ipython-input-31-71f19d55e4c3> in Paths()
2 class Paths(object):
3 root = attr.ib(default=os.getcwd())
--> 4 subdir_1= attr.ib(os.path.join(root, 'a'))
5 subdir_2= attr.ib(os.path.join(root, 'b'))
6

~/miniconda3/lib/python3.6/posixpath.py in join(a, *p)
76 will be discarded. An empty last part will result in a path that
77 ends with a separator."""
--> 78 a = os.fspath(a)
79 sep = _get_sep(a)
80 path = a

TypeError: expected str, bytes or os.PathLike object, not _CountingAttr









share|improve this question
























  • This is not really what attrs is for. What exactly are you trying to accomplish?

    – roeen30
    Nov 14 '18 at 22:16











  • @roeen30 I made significant changes to the code examples, which I hope more clearly illustrates the problem I was trying to solve.

    – truthling
    Nov 15 '18 at 22:47











  • It is clearer what you're trying to do, but not why. Idiomatic path operations are provided by the excellent pathlib - take a look at it if you haven't already. As the main goal of attrs is creating quick record classes, it doesn't really help you here, where you have a class with one pre-determined attribute (root). Looks like trying to fit a square peg through a round hole.

    – roeen30
    Nov 16 '18 at 19:09







  • 1





    As a side note, if you can't avoid using __get/setattribute__(), it is clearer to just use the getattr() and setattr() builtins.

    – roeen30
    Nov 16 '18 at 19:11














1












1








1


2






I want an object that represents a path root and an arbitrary number of subdirectories that are constructed with os.path.join(root). I want to access these paths with the form self.root, self.path_a, self.path_b, etc... In addition to accessing them directly via self.path_a, I want to be able to iterate over them. Unfortunately, the approach below does not allow iterating over them via attr.astuple(paths)



The first bit of code below is what I came up with. It works but feels a little hacky to me. Since this is my first use of attrs I am wondering if there is a more intuitive/idiomatic way to approach this. It took me quite a while to figure out how to write the fairly simple class below, so I thought I might be missing something obvious.



My approach



@attr.s
class Paths(object):
subdirs = attr.ib()
root = attr.ib(default=os.getcwd())
def __attrs_post_init__(self):
for name in self.subdirs:
subdir = os.path.join(self.root, name)
object.__setattr__(self, name, subdir)

def mkdirs(self):
"""Create `root` and `subdirs` if they don't already exist."""
if not os.path.isdir(self.root):
os.mkdir(self.root)
for subdir in self.subdirs:
path = self.__getattribute__(subdir)
if not os.path.isdir(path):
os.mkdir(path)


Output



>>> p = Paths(subdirs=['a', 'b', 'c'], root='/tmp')
>>> p
Paths(subdirs=['a', 'b', 'c'], root='/tmp')
>>> p.a
'/tmp/a'
>>> p.b
'/tmp/b'
>>> p.c
'/tmp/c'


The following was my first attempt, which doesn't work.



Failed attempt



@attr.s
class Paths(object):
root = attr.ib(default=os.getcwd())
subdir_1= attr.ib(os.path.join(root, 'a'))
subdir_2= attr.ib(os.path.join(root, 'b'))


Output



------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-31-71f19d55e4c3> in <module>()
1 @attr.s
----> 2 class Paths(object):
3 root = attr.ib(default=os.getcwd())
4 subdir_1= attr.ib(os.path.join(root, 'a'))
5 subdir_2= attr.ib(os.path.join(root, 'b'))

<ipython-input-31-71f19d55e4c3> in Paths()
2 class Paths(object):
3 root = attr.ib(default=os.getcwd())
--> 4 subdir_1= attr.ib(os.path.join(root, 'a'))
5 subdir_2= attr.ib(os.path.join(root, 'b'))
6

~/miniconda3/lib/python3.6/posixpath.py in join(a, *p)
76 will be discarded. An empty last part will result in a path that
77 ends with a separator."""
--> 78 a = os.fspath(a)
79 sep = _get_sep(a)
80 path = a

TypeError: expected str, bytes or os.PathLike object, not _CountingAttr









share|improve this question
















I want an object that represents a path root and an arbitrary number of subdirectories that are constructed with os.path.join(root). I want to access these paths with the form self.root, self.path_a, self.path_b, etc... In addition to accessing them directly via self.path_a, I want to be able to iterate over them. Unfortunately, the approach below does not allow iterating over them via attr.astuple(paths)



The first bit of code below is what I came up with. It works but feels a little hacky to me. Since this is my first use of attrs I am wondering if there is a more intuitive/idiomatic way to approach this. It took me quite a while to figure out how to write the fairly simple class below, so I thought I might be missing something obvious.



My approach



@attr.s
class Paths(object):
subdirs = attr.ib()
root = attr.ib(default=os.getcwd())
def __attrs_post_init__(self):
for name in self.subdirs:
subdir = os.path.join(self.root, name)
object.__setattr__(self, name, subdir)

def mkdirs(self):
"""Create `root` and `subdirs` if they don't already exist."""
if not os.path.isdir(self.root):
os.mkdir(self.root)
for subdir in self.subdirs:
path = self.__getattribute__(subdir)
if not os.path.isdir(path):
os.mkdir(path)


Output



>>> p = Paths(subdirs=['a', 'b', 'c'], root='/tmp')
>>> p
Paths(subdirs=['a', 'b', 'c'], root='/tmp')
>>> p.a
'/tmp/a'
>>> p.b
'/tmp/b'
>>> p.c
'/tmp/c'


The following was my first attempt, which doesn't work.



Failed attempt



@attr.s
class Paths(object):
root = attr.ib(default=os.getcwd())
subdir_1= attr.ib(os.path.join(root, 'a'))
subdir_2= attr.ib(os.path.join(root, 'b'))


Output



------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-31-71f19d55e4c3> in <module>()
1 @attr.s
----> 2 class Paths(object):
3 root = attr.ib(default=os.getcwd())
4 subdir_1= attr.ib(os.path.join(root, 'a'))
5 subdir_2= attr.ib(os.path.join(root, 'b'))

<ipython-input-31-71f19d55e4c3> in Paths()
2 class Paths(object):
3 root = attr.ib(default=os.getcwd())
--> 4 subdir_1= attr.ib(os.path.join(root, 'a'))
5 subdir_2= attr.ib(os.path.join(root, 'b'))
6

~/miniconda3/lib/python3.6/posixpath.py in join(a, *p)
76 will be discarded. An empty last part will result in a path that
77 ends with a separator."""
--> 78 a = os.fspath(a)
79 sep = _get_sep(a)
80 path = a

TypeError: expected str, bytes or os.PathLike object, not _CountingAttr






python python-attrs






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Nov 16 '18 at 18:06







truthling

















asked Nov 14 '18 at 22:12









truthlingtruthling

407




407












  • This is not really what attrs is for. What exactly are you trying to accomplish?

    – roeen30
    Nov 14 '18 at 22:16











  • @roeen30 I made significant changes to the code examples, which I hope more clearly illustrates the problem I was trying to solve.

    – truthling
    Nov 15 '18 at 22:47











  • It is clearer what you're trying to do, but not why. Idiomatic path operations are provided by the excellent pathlib - take a look at it if you haven't already. As the main goal of attrs is creating quick record classes, it doesn't really help you here, where you have a class with one pre-determined attribute (root). Looks like trying to fit a square peg through a round hole.

    – roeen30
    Nov 16 '18 at 19:09







  • 1





    As a side note, if you can't avoid using __get/setattribute__(), it is clearer to just use the getattr() and setattr() builtins.

    – roeen30
    Nov 16 '18 at 19:11


















  • This is not really what attrs is for. What exactly are you trying to accomplish?

    – roeen30
    Nov 14 '18 at 22:16











  • @roeen30 I made significant changes to the code examples, which I hope more clearly illustrates the problem I was trying to solve.

    – truthling
    Nov 15 '18 at 22:47











  • It is clearer what you're trying to do, but not why. Idiomatic path operations are provided by the excellent pathlib - take a look at it if you haven't already. As the main goal of attrs is creating quick record classes, it doesn't really help you here, where you have a class with one pre-determined attribute (root). Looks like trying to fit a square peg through a round hole.

    – roeen30
    Nov 16 '18 at 19:09







  • 1





    As a side note, if you can't avoid using __get/setattribute__(), it is clearer to just use the getattr() and setattr() builtins.

    – roeen30
    Nov 16 '18 at 19:11

















This is not really what attrs is for. What exactly are you trying to accomplish?

– roeen30
Nov 14 '18 at 22:16





This is not really what attrs is for. What exactly are you trying to accomplish?

– roeen30
Nov 14 '18 at 22:16













@roeen30 I made significant changes to the code examples, which I hope more clearly illustrates the problem I was trying to solve.

– truthling
Nov 15 '18 at 22:47





@roeen30 I made significant changes to the code examples, which I hope more clearly illustrates the problem I was trying to solve.

– truthling
Nov 15 '18 at 22:47













It is clearer what you're trying to do, but not why. Idiomatic path operations are provided by the excellent pathlib - take a look at it if you haven't already. As the main goal of attrs is creating quick record classes, it doesn't really help you here, where you have a class with one pre-determined attribute (root). Looks like trying to fit a square peg through a round hole.

– roeen30
Nov 16 '18 at 19:09






It is clearer what you're trying to do, but not why. Idiomatic path operations are provided by the excellent pathlib - take a look at it if you haven't already. As the main goal of attrs is creating quick record classes, it doesn't really help you here, where you have a class with one pre-determined attribute (root). Looks like trying to fit a square peg through a round hole.

– roeen30
Nov 16 '18 at 19:09





1




1





As a side note, if you can't avoid using __get/setattribute__(), it is clearer to just use the getattr() and setattr() builtins.

– roeen30
Nov 16 '18 at 19:11






As a side note, if you can't avoid using __get/setattribute__(), it is clearer to just use the getattr() and setattr() builtins.

– roeen30
Nov 16 '18 at 19:11













2 Answers
2






active

oldest

votes


















1














First attempt: you can't just attach random data to the class and hope that attrs (in this case astuple) will pick it up. attrs specifically tries to avoid magic and guessing, which means that you have to indeed define your attributes on the class.



Second attempt: you cannot use attribute name in the class scope (i.e. within class Paths: but outside of a method because – as Python tells you – at this point they are still internal data that is used by @attr.s.



The most elegant approach I can think of is a generic factory that takes the path as an argument and builds the full path:



In [1]: import attr

In [2]: def make_path_factory(path):
...: def path_factory(self):
...: return os.path.join(self.root, path)
...: return attr.Factory(path_factory, takes_self=True)


Which you can use like this:



In [7]: @attr.s
...: class C(object):
...: root = attr.ib()
...: a = attr.ib(make_path_factory("a"))
...: b = attr.ib(make_path_factory("b"))

In [10]: C("/tmp")
Out[10]: C(root='/tmp', a='/tmp/a', b='/tmp/b')

In [11]: attr.astuple(C("/tmp"))
Out[11]: ('/tmp', '/tmp/a', '/tmp/b')


attrs being attrs, you can of course go further and define your own attr.ib wrapper:



In [12]: def path(p):
...: return attr.ib(make_path_factory(p))

In [13]: @attr.s
...: class D(object):
...: root = attr.ib()
...: a = path("a")
...: b = path("b")
...:

In [14]: D("/tmp")
Out[14]: D(root='/tmp', a='/tmp/a', b='/tmp/b')





share|improve this answer






























    0














    Can't guess why would you like to access as self.paths.path. But, here is what i'd do:



    class D(object):
    root = os.getcwd()
    paths = dict()

    def __init__(self, paths=):
    self.paths.update('root': self.root)
    for path in paths:
    self.paths.update(path: os.path.join(self.root, path))

    def __str__(self):
    return str(self.paths)

    d = D(paths=['static', 'bin', 'source'])
    print(d)
    print(d.paths['bin'])


    output



    'root': '/home/runner', 'static': '/home/runner/static', 'bin': '/home/runner/bin', 'source': '/home/runner/source'
    /home/runner/bin


    You can make this more complex. Just an example. Hope it helps.






    share|improve this answer


















    • 1





      That would do the trick, but I'm interested in starting to use attrs.

      – truthling
      Nov 15 '18 at 22:48










    Your Answer






    StackExchange.ifUsing("editor", function ()
    StackExchange.using("externalEditor", function ()
    StackExchange.using("snippets", function ()
    StackExchange.snippets.init();
    );
    );
    , "code-snippets");

    StackExchange.ready(function()
    var channelOptions =
    tags: "".split(" "),
    id: "1"
    ;
    initTagRenderer("".split(" "), "".split(" "), channelOptions);

    StackExchange.using("externalEditor", function()
    // Have to fire editor after snippets, if snippets enabled
    if (StackExchange.settings.snippets.snippetsEnabled)
    StackExchange.using("snippets", function()
    createEditor();
    );

    else
    createEditor();

    );

    function createEditor()
    StackExchange.prepareEditor(
    heartbeatType: 'answer',
    autoActivateHeartbeat: false,
    convertImagesToLinks: true,
    noModals: true,
    showLowRepImageUploadWarning: true,
    reputationToPostImages: 10,
    bindNavPrevention: true,
    postfix: "",
    imageUploader:
    brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
    contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
    allowUrls: true
    ,
    onDemand: true,
    discardSelector: ".discard-answer"
    ,immediatelyShowMarkdownHelp:true
    );



    );













    draft saved

    draft discarded


















    StackExchange.ready(
    function ()
    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53309516%2fdynamically-define-attributes-that-depend-on-earlier-defined-attributes%23new-answer', 'question_page');

    );

    Post as a guest















    Required, but never shown

























    2 Answers
    2






    active

    oldest

    votes








    2 Answers
    2






    active

    oldest

    votes









    active

    oldest

    votes






    active

    oldest

    votes









    1














    First attempt: you can't just attach random data to the class and hope that attrs (in this case astuple) will pick it up. attrs specifically tries to avoid magic and guessing, which means that you have to indeed define your attributes on the class.



    Second attempt: you cannot use attribute name in the class scope (i.e. within class Paths: but outside of a method because – as Python tells you – at this point they are still internal data that is used by @attr.s.



    The most elegant approach I can think of is a generic factory that takes the path as an argument and builds the full path:



    In [1]: import attr

    In [2]: def make_path_factory(path):
    ...: def path_factory(self):
    ...: return os.path.join(self.root, path)
    ...: return attr.Factory(path_factory, takes_self=True)


    Which you can use like this:



    In [7]: @attr.s
    ...: class C(object):
    ...: root = attr.ib()
    ...: a = attr.ib(make_path_factory("a"))
    ...: b = attr.ib(make_path_factory("b"))

    In [10]: C("/tmp")
    Out[10]: C(root='/tmp', a='/tmp/a', b='/tmp/b')

    In [11]: attr.astuple(C("/tmp"))
    Out[11]: ('/tmp', '/tmp/a', '/tmp/b')


    attrs being attrs, you can of course go further and define your own attr.ib wrapper:



    In [12]: def path(p):
    ...: return attr.ib(make_path_factory(p))

    In [13]: @attr.s
    ...: class D(object):
    ...: root = attr.ib()
    ...: a = path("a")
    ...: b = path("b")
    ...:

    In [14]: D("/tmp")
    Out[14]: D(root='/tmp', a='/tmp/a', b='/tmp/b')





    share|improve this answer



























      1














      First attempt: you can't just attach random data to the class and hope that attrs (in this case astuple) will pick it up. attrs specifically tries to avoid magic and guessing, which means that you have to indeed define your attributes on the class.



      Second attempt: you cannot use attribute name in the class scope (i.e. within class Paths: but outside of a method because – as Python tells you – at this point they are still internal data that is used by @attr.s.



      The most elegant approach I can think of is a generic factory that takes the path as an argument and builds the full path:



      In [1]: import attr

      In [2]: def make_path_factory(path):
      ...: def path_factory(self):
      ...: return os.path.join(self.root, path)
      ...: return attr.Factory(path_factory, takes_self=True)


      Which you can use like this:



      In [7]: @attr.s
      ...: class C(object):
      ...: root = attr.ib()
      ...: a = attr.ib(make_path_factory("a"))
      ...: b = attr.ib(make_path_factory("b"))

      In [10]: C("/tmp")
      Out[10]: C(root='/tmp', a='/tmp/a', b='/tmp/b')

      In [11]: attr.astuple(C("/tmp"))
      Out[11]: ('/tmp', '/tmp/a', '/tmp/b')


      attrs being attrs, you can of course go further and define your own attr.ib wrapper:



      In [12]: def path(p):
      ...: return attr.ib(make_path_factory(p))

      In [13]: @attr.s
      ...: class D(object):
      ...: root = attr.ib()
      ...: a = path("a")
      ...: b = path("b")
      ...:

      In [14]: D("/tmp")
      Out[14]: D(root='/tmp', a='/tmp/a', b='/tmp/b')





      share|improve this answer

























        1












        1








        1







        First attempt: you can't just attach random data to the class and hope that attrs (in this case astuple) will pick it up. attrs specifically tries to avoid magic and guessing, which means that you have to indeed define your attributes on the class.



        Second attempt: you cannot use attribute name in the class scope (i.e. within class Paths: but outside of a method because – as Python tells you – at this point they are still internal data that is used by @attr.s.



        The most elegant approach I can think of is a generic factory that takes the path as an argument and builds the full path:



        In [1]: import attr

        In [2]: def make_path_factory(path):
        ...: def path_factory(self):
        ...: return os.path.join(self.root, path)
        ...: return attr.Factory(path_factory, takes_self=True)


        Which you can use like this:



        In [7]: @attr.s
        ...: class C(object):
        ...: root = attr.ib()
        ...: a = attr.ib(make_path_factory("a"))
        ...: b = attr.ib(make_path_factory("b"))

        In [10]: C("/tmp")
        Out[10]: C(root='/tmp', a='/tmp/a', b='/tmp/b')

        In [11]: attr.astuple(C("/tmp"))
        Out[11]: ('/tmp', '/tmp/a', '/tmp/b')


        attrs being attrs, you can of course go further and define your own attr.ib wrapper:



        In [12]: def path(p):
        ...: return attr.ib(make_path_factory(p))

        In [13]: @attr.s
        ...: class D(object):
        ...: root = attr.ib()
        ...: a = path("a")
        ...: b = path("b")
        ...:

        In [14]: D("/tmp")
        Out[14]: D(root='/tmp', a='/tmp/a', b='/tmp/b')





        share|improve this answer













        First attempt: you can't just attach random data to the class and hope that attrs (in this case astuple) will pick it up. attrs specifically tries to avoid magic and guessing, which means that you have to indeed define your attributes on the class.



        Second attempt: you cannot use attribute name in the class scope (i.e. within class Paths: but outside of a method because – as Python tells you – at this point they are still internal data that is used by @attr.s.



        The most elegant approach I can think of is a generic factory that takes the path as an argument and builds the full path:



        In [1]: import attr

        In [2]: def make_path_factory(path):
        ...: def path_factory(self):
        ...: return os.path.join(self.root, path)
        ...: return attr.Factory(path_factory, takes_self=True)


        Which you can use like this:



        In [7]: @attr.s
        ...: class C(object):
        ...: root = attr.ib()
        ...: a = attr.ib(make_path_factory("a"))
        ...: b = attr.ib(make_path_factory("b"))

        In [10]: C("/tmp")
        Out[10]: C(root='/tmp', a='/tmp/a', b='/tmp/b')

        In [11]: attr.astuple(C("/tmp"))
        Out[11]: ('/tmp', '/tmp/a', '/tmp/b')


        attrs being attrs, you can of course go further and define your own attr.ib wrapper:



        In [12]: def path(p):
        ...: return attr.ib(make_path_factory(p))

        In [13]: @attr.s
        ...: class D(object):
        ...: root = attr.ib()
        ...: a = path("a")
        ...: b = path("b")
        ...:

        In [14]: D("/tmp")
        Out[14]: D(root='/tmp', a='/tmp/a', b='/tmp/b')






        share|improve this answer












        share|improve this answer



        share|improve this answer










        answered Nov 16 '18 at 12:16









        hynekhynek

        1,30211018




        1,30211018























            0














            Can't guess why would you like to access as self.paths.path. But, here is what i'd do:



            class D(object):
            root = os.getcwd()
            paths = dict()

            def __init__(self, paths=):
            self.paths.update('root': self.root)
            for path in paths:
            self.paths.update(path: os.path.join(self.root, path))

            def __str__(self):
            return str(self.paths)

            d = D(paths=['static', 'bin', 'source'])
            print(d)
            print(d.paths['bin'])


            output



            'root': '/home/runner', 'static': '/home/runner/static', 'bin': '/home/runner/bin', 'source': '/home/runner/source'
            /home/runner/bin


            You can make this more complex. Just an example. Hope it helps.






            share|improve this answer


















            • 1





              That would do the trick, but I'm interested in starting to use attrs.

              – truthling
              Nov 15 '18 at 22:48















            0














            Can't guess why would you like to access as self.paths.path. But, here is what i'd do:



            class D(object):
            root = os.getcwd()
            paths = dict()

            def __init__(self, paths=):
            self.paths.update('root': self.root)
            for path in paths:
            self.paths.update(path: os.path.join(self.root, path))

            def __str__(self):
            return str(self.paths)

            d = D(paths=['static', 'bin', 'source'])
            print(d)
            print(d.paths['bin'])


            output



            'root': '/home/runner', 'static': '/home/runner/static', 'bin': '/home/runner/bin', 'source': '/home/runner/source'
            /home/runner/bin


            You can make this more complex. Just an example. Hope it helps.






            share|improve this answer


















            • 1





              That would do the trick, but I'm interested in starting to use attrs.

              – truthling
              Nov 15 '18 at 22:48













            0












            0








            0







            Can't guess why would you like to access as self.paths.path. But, here is what i'd do:



            class D(object):
            root = os.getcwd()
            paths = dict()

            def __init__(self, paths=):
            self.paths.update('root': self.root)
            for path in paths:
            self.paths.update(path: os.path.join(self.root, path))

            def __str__(self):
            return str(self.paths)

            d = D(paths=['static', 'bin', 'source'])
            print(d)
            print(d.paths['bin'])


            output



            'root': '/home/runner', 'static': '/home/runner/static', 'bin': '/home/runner/bin', 'source': '/home/runner/source'
            /home/runner/bin


            You can make this more complex. Just an example. Hope it helps.






            share|improve this answer













            Can't guess why would you like to access as self.paths.path. But, here is what i'd do:



            class D(object):
            root = os.getcwd()
            paths = dict()

            def __init__(self, paths=):
            self.paths.update('root': self.root)
            for path in paths:
            self.paths.update(path: os.path.join(self.root, path))

            def __str__(self):
            return str(self.paths)

            d = D(paths=['static', 'bin', 'source'])
            print(d)
            print(d.paths['bin'])


            output



            'root': '/home/runner', 'static': '/home/runner/static', 'bin': '/home/runner/bin', 'source': '/home/runner/source'
            /home/runner/bin


            You can make this more complex. Just an example. Hope it helps.







            share|improve this answer












            share|improve this answer



            share|improve this answer










            answered Nov 15 '18 at 2:24









            ChecheCheche

            813219




            813219







            • 1





              That would do the trick, but I'm interested in starting to use attrs.

              – truthling
              Nov 15 '18 at 22:48












            • 1





              That would do the trick, but I'm interested in starting to use attrs.

              – truthling
              Nov 15 '18 at 22:48







            1




            1





            That would do the trick, but I'm interested in starting to use attrs.

            – truthling
            Nov 15 '18 at 22:48





            That would do the trick, but I'm interested in starting to use attrs.

            – truthling
            Nov 15 '18 at 22:48

















            draft saved

            draft discarded
















































            Thanks for contributing an answer to Stack Overflow!


            • Please be sure to answer the question. Provide details and share your research!

            But avoid


            • Asking for help, clarification, or responding to other answers.

            • Making statements based on opinion; back them up with references or personal experience.

            To learn more, see our tips on writing great answers.




            draft saved


            draft discarded














            StackExchange.ready(
            function ()
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53309516%2fdynamically-define-attributes-that-depend-on-earlier-defined-attributes%23new-answer', 'question_page');

            );

            Post as a guest















            Required, but never shown





















































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown

































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown







            Popular posts from this blog

            Use pre created SQLite database for Android project in kotlin

            Darth Vader #20

            Ondo