How to get class method to update dictionary? - python

I'm trying to make a flexible argument parser which takes strings, finds arguments within them and creates a dictionary like this:
message = "--mass=12"
physics = comparse(message, "mass", "int", 10, "this is your mass attribute")
Produces: {'mass': 12}
I'm unable to update and add more arguments/keys (like if I wanted to add a 'vel' variable) to the dictionary. Unfortunately, I'm new to Python classes and although the parser detects the first argument, I'm unable to add to the dictionary. Here's my program:
import shlex
class comparse(object):
def __init__(self, message, attribute, var_type, default, help_txt):
self.data = {} #This is the "data" dictionary that this function will ultimately return.
self.message = message
self.attribute = attribute
self.var_type = var_type
self.default = default
self.help_txt = help_txt
#Remove unwanted symbols (e.g. "=", ":", etc.)
self.message = self.message.replace("=", " ")
self.message = self.message.replace(":", " ")
self.args = shlex.split(self.message)
def parse(self):
try:
options = {k.strip('-'): True if v.startswith('-') else v
for k,v in zip(self.args, self.args[1:]+["--"]) if k.startswith('-') or k.startswith('')}
if (self.var_type == "int"):
self.data[self.attribute] = int(options[self.attribute]) #Updates if "attribute" exists, else adds "attribute".
if (self.var_type == "str"):
self.data[self.attribute] = str(options[self.attribute]) #Updates if "attribute" exists, else adds "attribute".
except:
if self.attribute not in self.message:
if (self.var_type == "int"):
self.data[self.attribute] = int(self.default) #Updates if "x" exists, else adds "x".
if (self.var_type == "str"):
self.data[self.attribute] = str(self.default) #Updates if "x" exists, else adds "x".
return self.data
def add_argument(self):
self.data.update(self.data)
message = "-mass: 12 --vel= 18"
physics = comparse(message, "mass", "int", 10, "this is your mass attribute")
comparse.add_argument(message, "vel", "int", 10, "this is your velocity attribute")
print (physics.parse())
The comparse.add_argument method doesn't work. There's obviously something I'm doing wrong and classes generally confuse me! Could someone point out what's wrong with my program?

I'm a little confused about how your class is designed. I've answered the question in a more holistic way, with suggestions about how you should maybe redesign your class to achieve your goal better.
Typically, when you initialize a class (your parser) you pass it all the data that it needs to do its work. It seems, however, like your goal is to create a generic physics parser physics = comparse() then add possible arguments like mass and velocity to the physics parser's data. You then give the physics parser a message string like message = "-mass: 12 --vel= 18" for which it should parse and extract the arguments. This would suggest that the end of your code snippet, which is currently
message = "-mass: 12 --vel= 18"
physics = comparse(message, "mass", "int", 10, "this is your mass attribute")
comparse.add_argument(message, "vel", "int", 10, "this is your velocity attribute")
print (physics.parse())
should look like so:
message = "-mass: 12 --vel= 18"
# create a parser
physics = comparse()
# tell the parser what arguments it should accept
comparse.add_argument("vel", "int", 10, "this is your velocity attribute")
comparse.add_argument("mass", "int", 10, "this is your mass attribute")
# give the parser a specific message for which it should extract arguments
print (physics.parse(message))
This code snippet would create a parser, tell the parser what sorts of arguments it should accept (like velocity and mass), and then extract those arguments from a specific string message.
This design adheres better to object oriented programming principles. The benefits here are that you're creating a physics parser that can be reused, and then asking it to parse strings which it does not save in its own properties (no this.message). This way, you can make additional calls like `physics.parse("--mass: 40 --vel=15") and reuse the physics parser.
If this design fits your intention more accurately, I would modify the code in the following ways to adhere to it:
modify your init function so that it takes no parameters.
Since you have multiple arguments that you are storing within your
class, instead of having self.attribute, self.var_type, self.default, self.help_txt be just single variables, I would make them arrays that you can add the attribute names, variable types, defaults, and help texts to for EACH argument. Initialize each of these as empty arrays in init like so: self.defaults = []. I would also change the name of each to indicate that they're arraysand not individual variables, so defaults, types, texts etc.
Modify add_argument to be the following:
def add_argument(self. attribute, var_type, default, help_txt):
self.attributes.append(attribute)
self.var_types.append(var_type)
self.defaults.append(default)
self.help_txts.append(default)
Modify parser to take message as a parameter, remove its unwanted
symbols, perform the split, and then execute its logic for each of
the arguments you set up in add_argument.
Please comment if you have any questions, good luck!

in the following code from what you gave
def add_argument(self):
self.data.update(self.data)
the add_argument does not take any arguments.But, you have done the fllowing
comparse.add_argument(message, "vel", "int", 10, "this is your velocity attribute")
where you have given multiple arguments. this is the cause of the problem
To fix it, try modifying the add_argument function to accept the parameter's you want it to handle
EDIT: based on your comment, the function should have been
def add_argument(self,message, attribute, var_type, default, help_txt):
data = comparse.parse(message, attribute, var_type, default, help_txt)
self.data.update(data)
But, here again, parse method is actually taking no arguments in your code, so..modify it to accept all the arguments you need along with self

Related

How to correctly specify the type of the argument?

I have a ButtonTypes class:
class ButtonTypes:
def __init__(self):
self.textType = "text"
self.callbackType = "callback"
self.locationType = "location"
self.someAnotherType = "someAnotherType"
And a function that should take one of the attributes of the ButtonTypes class as an argument:
def create_button(button_type):
pass
How can I specify that the argument of the create_button function should not just be a string, but exactly one of the attributes of the ButtonTypes class?
Something like this:
def create_button(button_type: ButtonTypes.Type)
As far as I understand, I need to create a Type class inside the ButtonTypes class, and then many other classes for each type that inherit the Type class, but I think something in my train of thought is wrong.
It sounds like you actually want an Enum:
from enum import Enum
class ButtonTypes(Enum):
textType = "text"
callbackType = "callback"
locationType = "location"
someAnotherType = "someAnotherType"
def func(button_type: ButtonTypes):
# Use button_type
The enum specifies a closed set of options that the variable must be a part of.
Use an enumerated type.
from enum import Enum
class ButtonType:
TEXT = "text"
CALLBACK = "callback"
LOCATION = "location"
SOMETHINGELSE = "someOtherType"
def create_button(button_type: ButtonType):
...
This goes one step further: not only are there only 4 values of type ButtonType, but no string will work, even at runtime, since your function will either ignore the string value associated with the ButtonType value altogether, or use code to extract the string that will break if button_type is an arbitrary string.

Python Linking Variable Values

I want to to store a reference to a class object inside a dictionary or another class that will maintain the same value as the actual class object. Currently, I'm using a string of the class object and the eval() function. I know this isn't a proper solution but haven't found another fix.
curWeapon is a object of WeaponInfo with pveFlag and pvpFlag variables
ListSetting stores the boolean expression of curWeapon along with a,b,c...
wishLists is passed to the main class of the program which reads a file and changed the pve/pvpFlags
When a empty line is read the boolean expression is evaluated with the new flags
wishLists.append(ListSetting("curWeapon.pveFlag or not curWeapon.pvpFlag", a, b, c, d))
My only idea is making a new function that has separates the boolean expression from a,b,c.. when making the ListSetting and adding that separately. Although I'm not sure if wishLists would update the firstFlag, secondFlag... variables.
firstFlag = ListSetting(a,b,c,d)
wishLists.append(firstFlag)
def wishListFlags():
firstFlag.flags = curWeapon.pveFlag or not curWeapon.pvpFlag
secondFlag.flags = ""
...
I'm pretty sure that updating the index of wishLists would work but would need a bunch of if statements or a dictionary.
firstFlag = ListSetting(a,b,c,d)
wishLists.append(firstFlag)
flagExpressions = {
1 : curWishListcurWeapon.pveFlag or not curWeapon.pvpFlag,
2 : "",
...}
def wishListFlags():
for index in len(wishLists):
wishLists[index].flags = flagExpressions.get(index)
If anyone knows a better way to go about this please let me know. Also, if my examples aren't specific enough or are confusing I'd be happy to share my entire program, I didn't know if it would be too much.
To store an expression you use a function, which you later call to get the value of the expression.
flagExpressions = {
1: lambda: curWishListcurWeapon.pveFlag or not curWeapon.pvpFlag
2: lambda: ""
}
def wishListFlags():
for index in len(wishLists):
wishLists[index].flags = flagExpressions.get(index, lambda: None)()

FastQ programming error

So I'm trying to parse a FastQ sequence, but I'm a beginner to Python, and I'm a little confused as to why my code isn't working. This is what the program is supposed to carry out:
if I enter the FASTQ seqname line...
#EAS139:136:FC706VJ:2:2104:15343:197393
...then the program should output:
Instrument = EAS139
Run ID = 136
Flow Cell ID = FC706VJ
Flow Cell Lane = 2
Tile Number = 2104
X-coord = 15343
Y-coord = 197393
Here's my unfinished code thus far:
class fastq:
def __init__(self,str):
self.str = inStr.replace ('#',' ').split (':')
def lists (self,parameters):
self.parameters = ("Instrument","Run ID","Flow Cell ID","Flow Cell Lane","Tile Number","X-coordinates","y-coordinates")
def zip (self,myZip,zippedTuple):
self.Zip = zip(self.parameters,self.transform)
self.zippedTuple = tuple(myZip)
print (tuple(myZip))
def main():
seq = input('Enter FastQ sequence:')
new_fastq = fastq(str)
new_fastq.lists()
new_fastq.zip()
main()
The reason that your code isn't working is that it's more-or-less entirely wrong. To address your errors in the order we reach them when trying to run the program:
main:
new_fastq = fastq(str) does not pass the seq we just input, it passes the built-in string type;
__init__:
Calling the argument to fastq.__init__ str is a bad idea as it masks the very built-in we just tried to pass to it;
But whatever you call it, be consistent between the function definition and what is inside it - where do you think inStr is coming from?
lists:
Why is this separate to and not even called by __init__?
Why don't you pass any arguments?
What is the argument parameters even for?
zip:
Rather than define a method to print the object, it is more Pythonic to define fastq.__str__ that returns a string representation. Then you can print(str(new_fastq)). That being said;
Again, you mask a built-in. On this occasion, it's more of a problem because you actually try to use the built-in inside the method that masks it. Call it something else;
Again, you put unnecessary arguments in the definition, then don't bother to pass them anyway;
What is self.transform supposed to be? It is never mentioned anywhere else. Do you mean self.str (which, again, should be called something else, for reasons of masking a built-in and not actually being a string)?
myZip is one of the arguments you never passed, and I think you actually want self.Zip; but
Why would you create x = tuple(y) then on the next line print(tuple(y))? print(x)!
Addressing those points, plus some bonus PEP-008 tidying:
class FastQ:
def __init__(self, seq):
self.elements = seq.replace ('#',' ').split (':')
self.parameters = ("Instrument", "Run ID", "Flow Cell ID",
"Flow Cell Lane", "Tile Number",
"X-coordinates", "y-coordinates")
def __str__(self):
"""A rough idea to get you started."""
return "\n".join(map(str, zip(self.parameters, self.elements)))
def main():
seq = input('Enter FastQ sequence: ')
new_fastq = FastQ(seq)
print(str(new_fastq))
main()

Validate user input in python by a type reference

I'm implementing a little UserOptionHandler class in python. It goes something like this:
class OptionValue():
__init__(self, default_value, value_type=None):
self.value=default_value
if value_type == None:
self.value_type = type( default_value )
else:
self.value_type = value_type
class OptionHandler():
__init__(self, option_list):
self.options = {}
for opt_spec in option_list:
key, value, value_type = opt_spec
self.options[key] = Option( value, value_type )
def set(self, key, value):
opt = self.options[key]
opt.value = value
When I get user input to set the value for an option, I want to make sure that they've entered sane. Otherwise, the application run until it gets to a state where it uses the option, which may or may not crash it.
How do you do something like the following?
opt = handler.get( key )
user_input = input("Enter value for {0}:".format(key) )
if( magic_castable_test( user_input, opt.value_type ) ):
print "Your value will work!"
else:
print "You value will break things!"
i'm not sure i follow what you're asking, but i think that you just need to use type itself because:
user input is a string
many types, like int and float are functions/constructors that return an instance from a string. for example int("3") returns the integer 3.
so in your code, you would replace
if( magic_castable_test( user_input, opt.value_type ) ):
print "Your value will work!"
else:
print "You value will break things!"
with:
value = None
try:
value = opt.value_type(value)
except:
pass
if value is None:
print "Your value will work! It is", value
else:
print "Your value will not work
where i am assuming it's an error if either (1) an exception is thrown or (2) the type returning null.
and you can take this further and simply require that any types work this way. so, for example, you might have some information that is best represented as a custom class. then you should write the class so that it has an __init__ that takes a single string:
class MyClass:
def __init__(self, s):
# do something with string s here
and you can then pass MyClass as an option type to your code above.
ps here's perhaps a clearer example of what i mean:
>>> type(4)("3")
3
>>> type(type(4)("3"))
<type 'int'>
Put every separate type (of user options) into separate class!
Add methods that decide if raw user input can be expressed by type "tied" to class.
Make Option handler run raw user input through list of all possible types - classes.
' If any class say that raw user input can be translated into its value than you know that user input is valid. If more than one class can store user input, then there is ambiguity, and you did provided too little info to suggest you anything then.
'' If no class claim ability to store user input, then you know that user provided wrong input!
Get back to your methods and make sure that they are malicious-user-proof.
If validation is simple you can use re module (regular expresion) to quickly and simply extract valid values. However if validation is more complex then you need to implement it by other means (since complex regular expressions are ... complex)
are you looking for isinstance(user_input, opt.value_type)?
http://docs.python.org/library/functions.html#isinstance

How can I define a class in Python?

Quite simple, I'm learning Python, and I can't find a reference that tells me how to write the following:
public class Team {
private String name;
private String logo;
private int members;
public Team(){}
// Getters/setters
}
Later:
Team team = new Team();
team.setName("Oscar");
team.setLogo("http://....");
team.setMembers(10);
That is a class Team with the properties: name/logo/members
Edit
After a few attempts I got this:
class Team:
pass
Later
team = Team()
team.name = "Oscar"
team.logo = "http://..."
team.members = 10
Is this the Python way? It feels odd (coming from a strongly typed language of course).
Here is what I would recommend:
class Team(object):
def __init__(self, name=None, logo=None, members=0):
self.name = name
self.logo = logo
self.members = members
team = Team("Oscar", "http://...", 10)
team2 = Team()
team2.name = "Fred"
team3 = Team(name="Joe", members=10)
Some notes on this:
I declared that Team inherits from object. This makes Team a "new-style class"; this has been recommended practice in Python since it was introduced in Python 2.2. (In Python 3.0 and above, classes are always "new-style" even if you leave out the (object) notation; but having that notation does no harm and makes the inheritance explicit.) Here's a Stack Overflow discussion of new-style classes.
It's not required, but I made the initializer take optional arguments so that you can initialize the instance on one line, as I did with team and team3. These arguments are named, so you can either provide values as positional parameters (as with team) or you can use the argument= form as I did with team3. When you explicitly specify the name of the arguments, you can specify arguments in any order.
If you needed to have getter and setter functions, perhaps to check something, in Python you can declare special method functions. This is what Martin v. Löwis meant when he said "property descriptors". In Python, it is generally considered good practice to simply assign to member variables, and simply reference them to fetch them, because you can always add in the property descriptors later should you need them. (And if you never need them, then your code is less cluttered and took you less time to write. Bonus!)
Here's a good link about property descriptors: http://adam.gomaa.us/blog/2008/aug/11/the-python-property-builtin/
Note: Adam Gomaa's blog seems to have disappeared from the web. Here's a link to a saved copy at archive.org:
https://web.archive.org/web/20160407103752/http://adam.gomaa.us/blog/2008/aug/11/the-python-property-builtin/
It doesn't really matter if you specify values as part of the call to Team() or if you poke them into your class instance later. The final class instance you end up with will be identical.
team = Team("Joe", "http://example.com", 1)
team2 = Team()
team2.name = "Joe"
team2.logo = "http://example.com"
team2.members = 1
print(team.__dict__ == team2.__dict__)
The above will print True. (You can easily overload the == operator for Team instances, and make Python do the right thing when you say team == team2, but this doesn't happen by default.)
I left out one thing in the above answer. If you do the optional argument thing on the __init__() function, you need to be careful if you want to provide a "mutable" as an optional argument.
Integers and strings are "immutable". You can never change them in place; what happens instead is Python creates a new object and replaces the one you had before.
Lists and dictionaries are "mutable". You can keep the same object around forever, adding to it and deleting from it.
x = 3 # The name "x" is bound to an integer object with value 3
x += 1 # The name "x" is rebound to a different integer object with value 4
x = [] # The name "x" is bound to an empty list object
x.append(1) # The 1 is appended to the same list x already had
The key thing you need to know: optional arguments are evaluated only once, when the function is compiled. So if you pass a mutable as an optional argument in the __init__() for your class, then each instance of your class shares one mutable object. This is almost never what you want.
class K(object):
def __init__(self, lst=[]):
self.lst = lst
k0 = K()
k1 = K()
k0.lst.append(1)
print(k0.lst) # prints "[1]"
print(k1.lst) # also prints "[1]"
k1.lst.append(2)
print(k0.lst) # prints "[1, 2]"
The solution is very simple:
class K(object):
def __init__(self, lst=None):
if lst is None:
self.lst = [] # Bind lst with a new, empty list
else:
self.lst = lst # Bind lst with the provided list
k0 = K()
k1 = K()
k0.lst.append(1)
print(k0.lst) # prints "[1]"
print(k1.lst) # prints "[]"
This business of using a default argument value of None, then testing that the argument passed is None, qualifies as a Python design pattern, or at least an idiom you should master.
class Team:
def __init__(self):
self.name = None
self.logo = None
self.members = 0
In Python, you typically don't write getters and setters, unless you really have a non-trivial implementation for them (at which point you use property descriptors).
To write classes you would normally do:
class Person:
def __init__(self, name, age, height):
self.name = name
self.age = age
self.height = height
To instantiate instances of a class(es) you would do
person1 = Person("Oscar", 40, "6ft")
person2 = Team("Danny", 12, "5.2ft")
You can also set a default value:
class Person:
def __init__(self):
self.name = "Daphne"
self.age = 20
self.height = "5.4ft"
To instantiate a classes set like this, you want to do:
person3 = Person()
person3.name = "Joe"
person3.age = 23
person3.height = "5.11ft"
You will notice that this method bears a lot of similarity to your typical Python dictionary interaction.

Categories

Resources