简体   繁体   中英

Python equivalent of PHP's @

Is there a Python equivalent of PHP's @ ?

@function_which_is_doomed_to_fail();

I've always used this block:

try:
  foo()
except:
  pass

But I know there has to be a better way.

Does anyone know how I can Pythonicify that code?


I think adding some context to that code would be appropriate:

for line in blkid:
  line = line.strip()
  partition = Partition()

  try:
    partition.identifier = re.search(r'^(/dev/[a-zA-Z0-9]+)', line).group(0)
  except:
    pass

  try:
    partition.label = re.search(r'LABEL="((?:[^"\\]|\\.)*)"', line).group(1)
  except:
    pass

  try:
    partition.uuid = re.search(r'UUID="((?:[^"\\]|\\.)*)"', line).group(1)
  except:
    pass

  try:
    partition.type = re.search(r'TYPE="((?:[^"\\]|\\.)*)"', line).group(1)
  except:
    pass

  partitions.add(partition)

What you are looking for is anti-pythonic, because:

The Zen of Python, by Tim Peters
Beautiful is better than ugly.

Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.

Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than right now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!

In your case, I would use something like this:

match = re.search(r'^(/dev/[a-zA-Z0-9]+)', line)
if match:
    partition.identifier = match.group(0)

And you have 3 lines instead of 4.

There is no better way. Silently ignoring error is bad practice in any language, so it's naturally not Pythonic.

Please don't ask for Python to be like PHP. You should always explicitly trap the most specific error you can. Catching and ignoring all errors like that is not good best practice. This is because it can hide other problems and make bugs harder to find. But in the case of REs, you should really check for the None value that it returns. For example, your code:

label = re.search(r'LABEL="((?:[^"\\]|\.)*)"', line).group(1)

Raises an AttributeError if there is not match, because the re.search returns None if there is no match. But what if there was a match but you had a typo in your code:

label = re.search(r'LABEL="((?:[^"\\]|\.)*)"', line).roup(1)

This also raises an AttributeError, even if there was a match. But using the catchall exception and ignoring it would mask that error from you. You will never match a label in that case, and you would never know it until you found it some other way, such as by eventually noticing that your code never matches a label (but hopefully you have unit tests for that case...)

For REs, the usual pattern is this:

matchobj = re.search(r'LABEL="((?:[^"\\]|\.)*)"', line)
if matchobj:
    label = matchobj.group(1)

No need to try and catch an exception here since there would not be one. Except... when there was an exception caused by a similar typo.

Use data-driven design instead of repeating yourself. Naming the relevant group also makes it easier to avoid group indexing bugs:

_components = dict(
  identifier = re.compile(r'^(?P<value>/dev/[a-zA-Z0-9]+)'),
  label = re.compile(r'LABEL="(?P<value>(?:[^"\\]|\\.)*)"'),
  uuid = re.compile(r'UUID="(?P<value>(?:[^"\\]|\\.)*)"'),
  type = re.compile(r'TYPE="(?P<value>(?:[^"\\]|\\.)*)"'),
)

for line in blkid:
    line = line.strip()
    partition = Partition()

    for name, pattern in _components:
        match = pattern.search(line)
        value = match.group('value') if match else None
        setattr(partition, name, value)

    partitions.add(partition)

Building upon Gabi Purcanu's answer and your desire to condense to one-liners, you could encapsulate his solution into a function and reduce your example:

def cond_match(regexp, line, grp):
    match = re.search(regexp, line)
    if match:
        return match.group(grp)
    else:
        return None

for line in blkid:
    line = line.strip()
    partition = Partition()
    partition.identifier = cond_match(r'^(/dev/[a-zA-Z0-9]+)', line, 0)
    partition.label = cond_match(r'LABEL="((?:[^"\\]|\\.)*)"', line, 1)
    partition.uuid = cond_match(r'UUID="((?:[^"\\]|\\.)*)"', line, 1)
    partition.type = cond_match(r'TYPE="((?:[^"\\]|\\.)*)"', line, 1)
    partitions.add(partition)

There is warnings control in Python - http://docs.python.org/library/warnings.html

After edit:

You probably want to check if it is not None before trying to get the groups. Also use len() on the groups to see how many groups you have got . "Pass"ing the error is definitely not the way to go.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM