How to write a method like islower , isupper? - python

I want to write a method which i can use with strings same like we use "word".isupper return False, "word".islower return True, so I want to define a class and want to use like "word".isspecial()
so if i have a list of special characters :
special=["&","*",")("]
and I want that if I have a word I can check like ")(".method() and it return True as we do with "string".islower() which return True
I know I can do this with simple loop and in operator but I want to know how I can do like ")(".method()
What I tried is buggy:
class Lower:
def check(self,x):
for i in lower:
if x==i:
return True
check1=Lower()
check1.check()
print(check1.check(special,")("))

You can write a function to do what you want like this:
import string
special_set = set(string.punctuation)
def isspecial(s):
return all(c in special_set for c in s)
print(isspecial(")("))
print(isspecial(")(*)(&)(*&)*&"))
print(isspecial(")(*)(&)(*&)*&x"))
This will output, as expected:
True
True
False
Note I've used string.punctuation to build the set of "special characters". You can define this set by hand if you like.
Really, this is the Pythonic way to do what you want - there's no reason to have to have a method doing this. If you really insist, you can look at this question for more information, but beware it will become messy.

Related

Is using min/max on boolean lists in Python bad?

So I have a condition I want to happen only when all items in a list evaluate to true. An example of this might be something like a list of functions which return a boolean (not actually my situation but easier to explain):
def foo():
# do something
def bar():
# do something
def baz():
# do something
fncs = [foo, bar, baz]
if <every element in [fnc() for fnc in fncs] evaluates to true>:
# do something
I know I can definitely do this:
all_true = True
for fnc in fncs:
if not fnc():
all_true = False
if all_true:
# do something
But, I was wondering if this is poor form:
if min([fnc() for fnc in fncs]):
# do something
Alternatively, I could try to select all false elements during list comprehension and check whether the list is occupied (this works because bool(arr) where arr is a list returns False if and only if arr is empty):
if not [None for fnc in fncs if not fnc()]:
# do something
I feel like the "min" is a lot cleaner than the last method, and while the first one is easiest to understand for novices it should be pretty clear what's going on. I could alternately make it clearer through aliasing:
all_true = min
if all_true([fnc() for fnc in fncs]):
# do something
I'm sure I missed other ways to do this as well. I'm wondering what method would be most desirable.
Thanks for the help!
Using min is not "bad", but it is unclear what you are trying to do and will do unnecessary work. You should use all instead.
if all(fnc() for fnc in fncs):
# do something
This will return True if all values are True, and False otherwise. It has the added benefit of quitting early if any value is False, unlike min which must run through the entire sequence at least once.
If you find that you need to evaluate if the are all False, I recommend any (well, not any(...), specifically) which has a similar "quit as soon as possible" behavior.
You're probably looking for all built-in function:
print all(f() for f in [foo, bar, baz])

Proper use of lambda in function definition

I want to write a simple function that recognizes palindromes:
>>> def palindrome(s):
return s == s[::-1]
It works fine but it is case sensitive and to fix that I could do:
>>> def palindrome(s):
return s.lower() == s[::-1].lower()
>>> palindrome('Aba')
True
but I figure it's not very elegant. I tried to lowercase the input by using lambda expressions but I am doing something wrong and don't know how to fix it:
>>> def palindrome(lambda s : s.lower()):
return s == s[::-1]
SyntaxError: invalid syntax
You cannot use a lambda expression to describe actions that should be performed on input parameters (you can however use lambda to define a default value). You can do two things:
Define a function in the head of the function:
def palindrome(s):
s = s.lower()
return s == s[::-1]
Use a decorator:
def caseinsensitive(f):
def helper(s):
s = s.lower()
return f(s)
return helper
and then define your palindrome as:
#caseinsensitive
def palindrome(s):
return s == s[::-1]
Here you can reuse the #caseinsensitive to define all functions that do this as a first step.
Just call lower once, reassign s to the value and forget the lambda:
def palindrome(s):
s = s.lower()
return s == s[::-1]
This isn't really idiomatic python, but what you're looking for is something like this:
def palindrome(s):
return (lambda x: x == x[::-1])(s.lower())
That is, you define a lambda function and immediately invoke it, binding s.lower() to x.
def palindrome(s):
s = s.lower()
return s == s[::-1]
This is pretty straightforward and easy to use and understand answer, which is 100% correct and good.
BUT if you want to use lambda expression you must think how and what and why and stuff so let's go into the magical world of FUNCTIONAL PROGRAMMING.
If you don't know what a lambda expression is, basically when you type in the word lambda it specifies that you will later on give it some value for instance typing lambda a means you will supply it with 1 value (argument), typing lambda a, b explicitly means you will suppliy it with 2 values (arguments). So now that this whole thing of "what does even this lambda word mean" is done let's go deeper into the magical world of FUNCTIONAL PROGRAMMING.
So now when you tell python that it will have to wait some time (or maybe no time at all) for that value so it can do some magic on it, you can tell it what to do with it for instance
some_var = lambda some_string: some_string.lower()
So now this means that it's going to get some value, we expect it to be some sort of string and we can and will hold it in some_var for reasons only PHP programmers and us (me) know.
Next up is really straight forward we just return the check whether it is or not a palindrome
return some_var == some_var[::-1]
Let's get some glue and build this lambda beast from the things we have earlier
def palindrome():
some_var = lambda some_string : some_string.lower()
return some_var == some_var[::-1]
As you can see we no longer need to declare that we use some puny s in the method, hence we just press DEL and we can go along into the beatiful world of FUNCTIONAL PROGRAMMING.
So let's try to call this function, but the question raises how to do it?
palindrome("superpalindrome") == False
It does not compile though, because it thinks we are trying to give the palindrome method some kind of an argument while the definition has none at all. So the correct call of the function should be
palindrome()("superpalindrome") == False
In short, this is just magic, lambda expressions are actually in most cases worse in case of time usage, so you should stick to doing stuff in a OOP way or even else pythonic way. If you want to use lambda expressions you should try switching to Haskell(which I strongly advise) or Scala. If you have any further questions, feel free to ask me, I love talking about Haskell. Or FUNCTIONAL PROGRAMMING.
Full answer that is even more simplified
def palindrome():
return lambda some_str : some_str.lower() == some_str.lower()[::-1]
method = palindrome()
print(method("cococ"))
Maybe you wanted this:
(lambda lstr : lstr == lstr[::-1])((lambda x : x.lower())('abA'))

Check if a string is in List case-insentive [duplicate]

I love using the expression
if 'MICHAEL89' in USERNAMES:
...
where USERNAMES is a list.
Is there any way to match items with case insensitivity or do I need to use a custom method? Just wondering if there is a need to write extra code for this.
username = 'MICHAEL89'
if username.upper() in (name.upper() for name in USERNAMES):
...
Alternatively:
if username.upper() in map(str.upper, USERNAMES):
...
Or, yes, you can make a custom method.
str.casefold is recommended for case-insensitive string matching. #nmichaels's solution can trivially be adapted.
Use either:
if 'MICHAEL89'.casefold() in (name.casefold() for name in USERNAMES):
Or:
if 'MICHAEL89'.casefold() in map(str.casefold, USERNAMES):
As per the docs:
Casefolding is similar to lowercasing but more aggressive because it
is intended to remove all case distinctions in a string. For example,
the German lowercase letter 'ß' is equivalent to "ss". Since it is
already lowercase, lower() would do nothing to 'ß'; casefold()
converts it to "ss".
I would make a wrapper so you can be non-invasive. Minimally, for example...:
class CaseInsensitively(object):
def __init__(self, s):
self.__s = s.lower()
def __hash__(self):
return hash(self.__s)
def __eq__(self, other):
# ensure proper comparison between instances of this class
try:
other = other.__s
except (TypeError, AttributeError):
try:
other = other.lower()
except:
pass
return self.__s == other
Now, if CaseInsensitively('MICHAEL89') in whatever: should behave as required (whether the right-hand side is a list, dict, or set). (It may require more effort to achieve similar results for string inclusion, avoid warnings in some cases involving unicode, etc).
Usually (in oop at least) you shape your object to behave the way you want. name in USERNAMES is not case insensitive, so USERNAMES needs to change:
class NameList(object):
def __init__(self, names):
self.names = names
def __contains__(self, name): # implements `in`
return name.lower() in (n.lower() for n in self.names)
def add(self, name):
self.names.append(name)
# now this works
usernames = NameList(USERNAMES)
print someone in usernames
The great thing about this is that it opens the path for many improvements, without having to change any code outside the class. For example, you could change the self.names to a set for faster lookups, or compute the (n.lower() for n in self.names) only once and store it on the class and so on ...
Here's one way:
if string1.lower() in string2.lower():
...
For this to work, both string1 and string2 objects must be of type string.
I think you have to write some extra code. For example:
if 'MICHAEL89' in map(lambda name: name.upper(), USERNAMES):
...
In this case we are forming a new list with all entries in USERNAMES converted to upper case and then comparing against this new list.
Update
As #viraptor says, it is even better to use a generator instead of map. See #Nathon's answer.
You could do
matcher = re.compile('MICHAEL89', re.IGNORECASE)
filter(matcher.match, USERNAMES)
Update: played around a bit and am thinking you could get a better short-circuit type approach using
matcher = re.compile('MICHAEL89', re.IGNORECASE)
if any( ifilter( matcher.match, USERNAMES ) ):
#your code here
The ifilter function is from itertools, one of my favorite modules within Python. It's faster than a generator but only creates the next item of the list when called upon.
To have it in one line, this is what I did:
if any(([True if 'MICHAEL89' in username.upper() else False for username in USERNAMES])):
print('username exists in list')
I didn't test it time-wise though. I am not sure how fast/efficient it is.
Example from this tutorial:
list1 = ["Apple", "Lenovo", "HP", "Samsung", "ASUS"]
s = "lenovo"
s_lower = s.lower()
res = s_lower in (string.lower() for string in list1)
print(res)
My 5 (wrong) cents
'a' in "".join(['A']).lower()
UPDATE
Ouch, totally agree #jpp, I'll keep as an example of bad practice :(
I needed this for a dictionary instead of list, Jochen solution was the most elegant for that case so I modded it a bit:
class CaseInsensitiveDict(dict):
''' requests special dicts are case insensitive when using the in operator,
this implements a similar behaviour'''
def __contains__(self, name): # implements `in`
return name.casefold() in (n.casefold() for n in self.keys())
now you can convert a dictionary like so USERNAMESDICT = CaseInsensitiveDict(USERNAMESDICT) and use if 'MICHAEL89' in USERNAMESDICT:

Pythonic way to get boolean value of object

I'd like opinions on the most idiomatic way to write a function that returns True if an object is truthy, and False otherwise. For example:
is_truthy(True) # True
is_truthy(False) # False
is_truthy(123) # True
is_truthy(0) # False
is_truthy("some string") # True
is_truthy("") # False
The best I've come up with is this:
def is_truthy(obj):
return not not obj
Can anybody do better?
is_truthy = bool
The builtins got you covered.
You can do it like this:
bool(obj)
If you need a bool it's because you will end up using it in if statements and the like. I don't think you need to encapsulate anything within an is truthy function; just use the bool directly. I.e. rather than:
if is_truthy(my_bool):
# do something
simply do:
if my_bool:
# do something

python convert a string to an operator

Is it possible to convert a string to an operator in python?
I would like to pass a condition to a function
Ideally it would look like this:
def foo(self, attribute, operator_string, right_value):
left_value = getattr(self, attribute)
if left_value get_operator(operator_string) right_value:
return True
else:
return False
bar.x = 10
bar.foo('x', '>', 10)
[out] False
bar.foo('x', '>=', 10)
[out] True
I could make a dictionary where keys are strings and values are functions of the operator module.
I would have to change foo definition slightly:
operator_dict = {'>', operator.lt,
'>=', operator.le}
def foo(self, attribute, operator_string, right_value):
left_value = getattr(self, attribute)
operator_func = operator_dict[operator_string]
if operator_func(left_value, right_value):
return True
else:
return False
This means I have to make this dictionary, but is it really necessary?
You can use eval to dynamically build a piece of Python code and execute it, but apart from that there are no real alternatives. The dictionary-based solution is much more elegant and safe, however.
Apart from that, is it really that bad? Why not shorten it a bit …
return operator_dict[operator_string](left_value, right_value)
The way the problem is specified I don't see why you can't pass operator.le to the function instead of ">=".
If this operator_string coming from a database or file or something or are you passing it around in your code?
bar.foo('x', operator.le , 10)
Are you just looking to have a convenient shorthand? Then you might do something like:
from operator import le
bar.foo('x', le, 10)
If the real problem here is that you have code or business rules coming in from a database or datafile then maybe you actually need to look at writing a little parser that will map your input into these objects and then you could take a look at using a library like pyparsing, ply, codetalker, etc.
#This is very simple to do with eval()
score=1
trigger_conditon=">="
trigger_value=4
eval(f"{score}{trigger_conditon}{trigger_value}")
#luckily fstring also takes care of int/float or relavaent datatype
operator_str="ge"
import operator
eval(f"operator.{operator_str}({score},{trigger_value})")

Categories