I struggle with using variables (ultimately dictionary) for dynamically compose and access class attributes using getattr:
from gpiozero import PiStop
lights = PiStop('A+')
# working call: lights.red.on()
var = 'red.on'
getattr(lights(), var) # doesn't work - error
I cannot find proper syntax...
You have two attributes being accessed; lights.red is one such attribute, and on the result of that access, you then apply another attribute access, so <result>.on.
You need to use separate getattr() calls to achieve the same.
You could split on the '.' dot in var and apply each name separately, in a loop:
result = lights()
for name in var.split('.'):
result = getattr(result, name)
This allows for var to be set to any number of nested attributes.
Related
I have a dataframe listing individual names, and for each one I have an identically-named object storing attributes about them. However, when iterating through the dataframe, I don't know how to use the listed name to access the correct object (which I want to pull an attribute from).
person = df._get_value(n, 'People')
getattr(person, 'Age')
When I run this, I get an Attribute Error because its attempting to access 'Age' in the string, but not the matching object. How should I convert the string dynamically so I can access the correct object?
You can use inbuilt function import (mind the double underscore). Sample code below
module = __import__("name of the package", globals(), locals(), ["moduleName"], 0)
obj_class = getattr(module, "moduleName")
cclass = obj_class()
getattr(cclass, "attributeName")
You can view this doc for more details on importlib
Within a class in Python:
I have this variable that I pass through a function called "changers"
energy_L_gainer = changers('cap_largeover,','sec_energy','-change')
When I call upon this variable from another function, how can I get it to pass back through the function again to refresh the data in the variable. I have searched everywhere and tried all that my small mind could muster, and I cannot get it to work. Any ideas? Thank you all so much.
Can you rerun the class somehow within the function to refresh the variables?
I called upon this variable within another function using:
self.energy_L_gainer
Here is the changers function for reference:
def changers(cap, sec, sort):
screen = requests.get(f'https://finviz.com/screener.ashx?v=111&f={cap}{sec},geo_usa&ft=4&o={sort}', headers = headers).text
tables = pd.read_html(screen)
tables = tables[-2]
tables.columns = tables.iloc[0]
tables = tables[1:]
return tables
Make it a property on your class
#property
def energy_L_gainer(self):
return changers('cap_largeover,','sec_energy','-change')
Now any reference to self.energy_L_gainer will use the result of that function
I'm not quite sure I'm following you, but you probably need to use self.energy_L_gainer = instead of energy_L_gainer =. In Python those two aren't the same like they can be in some other languages.
Currently I am working on a selenium project which I am integrated with Jenkins.I have stored Locators in a class. So my target is to take input from jenkins and use that variable as a key to get value from the
class Locators(object):
rundate = 'PREV' # This value is user input, either PREV or NOW
PREV = 'abcd'
NOW = 'bcd'
So I want to use it as:
Test = Locators()
Test.(Test.rundate)
Dotted attribute access always requires a valid identifier for the attribute name, not an arbitrary expression. Use getattr in other situations.
getattr(Test, Test.rundate)
I'm trying to use Factoryboy to create a list in an object of the length specified when created.
I can create the list, but every attempt to create a list with the length specified causes issues due to the lazy nature of the provided length/size.
This is what I have so far:
class FooFactory(factory.Factory):
class Meta:
model = command.Foo
foo_uuid = factory.Faker("uuid4")
bars = factory.List([
factory.LazyAttribute(lambda o: BarFactory()
for _ in range(3))
])
This will create a list of 3 random Bars. I have tried using a combination of Params and exclude, but because range expects an Int, and the int won't be lazily loaded until later, it causes an error.
I would like something similar to how one to many relationships are generated with post_generation ie.
foo = FooFactory(number_of_bars=5)
Anyone had any luck with this?
Main solution
Two things are needed for this:
parameters
and LazyAttribute
(the links point to their documentation, for more detail).
Parameters are like factory attributes that are not passed to the instance that will be created.
In this case, they provide a way to parametrize the length of the list of Bars.
But in order to use parameters to customize a field in the factory, we need to have access to self,
that is, the instance being built.
We can achieve that with LazyAttribute, which is a declaration that takes a function with one argument:
the object being built.
Just what we needed.
So the snippet in the question could be re-written as follows:
class FooFactory(factory.Factory):
class Meta:
model = command.Foo
class Params:
number_of_bars = 1
foo_uuid = factory.Faker("uuid4")
bars = factory.LazyAttribute(lambda self: [BarFactory()] * self.number_of_bars)
And used like this:
foo = FooFactory(number_of_bars=3)
If the number_of_bars argument is not provided, the default of 1 is used.
Drawbacks
Sadly, there is a some limitation to what we can do here.
The preferred way to use a factory in the definition of another factory is via
SubFactory.
That is preferred for two reasons:
it respects the build strategy used for the parent factory
it collects extra keyword arguments to customize the subfactory
The first one means that if we used SubFactory to build a Bar in FooFactory
and called FooFactory with FooFactory.create or FooFactory.build,
the Bar subfactory would respect that and use the same strategy.
In summary, the build strategy only builds an instance,
while the create strategy builds and saves the instance to the persistent storage being used,
for example a database, so respecting this choice is important.
See the docs
for more details.
The second one means that we can directly customize attributes of Bar when calling FooFactory.
For example:
foo = FooFactory(bar__id=2)
would set the id of the bar of foo to be 2 instead of what the Bar subfactory would generate by default.
But I could not find a way to use SubFactory and a dynamic length via Params.
There is no way, as far as I know, to access the value of a parameter in a context where FactoryBoy expects a SubFactory.
The problem is that the declarations that give us access to the object being built always expect a final value to be returned,
not another factory to be called later.
This means that, in the example above, if we write instead:
class FooFactory(factory.Factory):
# ... rest of the factory
bars = factory.LazyAttribute(lambda self: [factory.SubFactory(BarFactory)] * self.number_of_bars)
then calling it like
foo = FooFactory(number_of_bars=3)
would result in a foo that has a list of 3 BarFactory in foo.bars instead of a list of 3 Bars.
And using SelfAttribute,
which is a way to reference another attribute of the instance being built, doesn't work either
because it is not evaluated before the rest of the expression in a declaration like this:
class FooFactory(factory.Factory):
# ... rest of the factory
bars = factory.List([factory.SubFactory(BarFactory)] * SelfAttribute("number_of_bars"))
That raises TypeError: can't multiply sequence by non-int of type 'SelfAttribute'.
A possible workaround is to call BarFactory beforehand and pass it to FooFactory:
number_of_bars = 3
bars = BarFactory.create_batch(number_of_bars)
foo = FooFactory(bars=bars)
But that's certainly not as nice.
Another one that I found out recently is RelatedFactoryList.
But that's still experimental and it doesn't seem to have a way to access parameters.
Additionally, since it's generated after the base factory, it also might not work if the instance constructor
expects that attribute as an argument.
There is a way to pass the length of a list and retain the ability to set additional properties on the subfactory. It requires creating a post_generation method.
class FooFactory(factory.Factory):
class Meta:
model = command.Foo
foo_uuid = factory.Faker("uuid4")
bars__count = 5 # Optional: default number of bars to create
#factory.post_generation
def bars(self, create, extracted, **kwargs):
if not create:
return
num_bars = kwargs.get('count', 0)
color = kwargs.get('color')
if num_bars > 0:
self.bars = [BarFactory(color=color)] * num_bars
elif extracted:
self.bars=extracted
any parameter with the construct modelname__paramname will be passed to the post_generation method as paramname in kwargs.
You can then call the FooFactory as:
FooFactory.create(bars__color='blue')
and it will create Foo with 5 Bars (the default value).
You can also call FooFactory and tell it to create 10 Bars.
FooFactory.create(bars__color='blue', bars__count=10)
I have a Python class , having some variables. The definition of the class is as follows:
class student:
def __init__(self,name,rollno,DOB,branch):
self.name=name
self.rollno=rollno
self.DOB=DOB
self.branch=branch
self.books=[]
self.fines=[]
I am adding new attributes for a student , and need to store the corresponding values as well (for future use). This is done using the setattr method, and works fine.
Code snippet:
setattr(student,"date_of_join",date())
Now I am approaching the problem as, if the user adds a new attribute (say, "date_of_join"), then I update a list (studattr), initially containing ["name","rollno",DOB","branch","books","fines"] to update the list of attributes. This means that the updated list will also have "date_of_join" now appended to it.
Now if I want to access this list of attributes of a student Instance, then how do I do it? ( Since the records are dynamically updated, and let us suppose I have to access x.date_of_join, then how do I join the two strings? Is there anything similar to python's os.path.join, or rather linux's system call, (merging two strings)? )
Problem:
for attribute in studattr:
print attribute,x.attribute
{ This throws an Exception since the instance x has no variable or method named "attribute")
PS: I tried using inspect,inspect.getmembers(x) as well as dirs(x), vars(x) ,(mentioned on stackoverflow), but it only gives me a list of variables/methods in main class body and not in init.
Use getattr() to access dynamic attributes:
for attr in student.studattr:
print attr, getattr(student, attr)
or use vars() will give you a dictionary of current attributes on a student:
for attr, value in vars(student).iteritems():
print attr, value