0

I have a simple code as below:

for link in links:
    products[link] = get_products(link)

I am expecting links to be a set of strings. However, sometimes it is a single link and Python seems to break it down to individual characters in the string, which is not what I want. How can I make it treat links as a set of strings even if it is a single string?

Update: Links comes from:

def read_from_file(name):
    with open(name, 'r') as file:
        links = file.read()
        links_set = links.split('\n')    
    return links_set

And the file contains one link per line.

Jorjani
  • 827
  • 1
  • 16
  • 31
  • 1
    Try with `split()` – Rohit-Pandey Oct 29 '18 at 19:21
  • 2
    How is `links` created? If it really is a set, then it shouldn't iterate over individual letters of its elements, even if it contains only one value. I suspect that whatever code is responsible for creating `links` doesn't consistently return the same type of object every time. Please provide a [mcve]. – Kevin Oct 29 '18 at 19:22
  • 2
    You might find some useful information reading responses to [this answer](https://stackoverflow.com/questions/9168904/suppressing-treatment-of-string-as-iterable) for some background on potential workarounds. – vielkind Oct 29 '18 at 19:24
  • @Kevin. OP's example is plenty complete for the question being asked. The fact that there may be an XY problem here (which I completely agree with), does not alter the completeness of the question. – Mad Physicist Oct 29 '18 at 19:36
  • @Kevin. But with the last update, I am glad you asked. – Mad Physicist Oct 29 '18 at 19:37
  • 1
    @Jorjani. You are definitely missing something here. The code you just showed will *never* return a set. It won't even return a list, just a string. What are you really doing that gets you a set *sometimes*? – Mad Physicist Oct 29 '18 at 19:38
  • @MadPhysicist I think there was a missing line that I included now. The code now returns a collection (list?). My problem was when there was only one line. – Jorjani Oct 29 '18 at 19:42
  • @Jorjani. Now you have a `list` (always), but not a `set`. How do you get a string *sometimes*? – Mad Physicist Oct 29 '18 at 19:42
  • Thanks for clarifying. I was treating them as a collection so a set or list doesn't matter for my case. As for the scenario where I get a single string, I'm not sure, which is why I want to find a way to prevent it from breaking down the single string if it is the case. – Jorjani Oct 29 '18 at 19:47
  • @Jorjani. I would recommend making your terminology precise in the future, but aside from that, I believe my answer is pretty complete. – Mad Physicist Oct 29 '18 at 19:48
  • Noted. And thank you. I had ended up doing a longer version of your answer but I was hoping there would be an even easier answer. I guess Java has made me lazy! – Jorjani Oct 29 '18 at 19:50

3 Answers3

5

If link can be a set or a single str element, use isinstance to check:

for link in {links} if isinstance(links, str) else links:

This is a code smell that indicates that the procedure returning links is inconsistent and possibly has other issues.

I also don't generally recommend using isinstance for anything, since Python is very good with duck-typing. Strings may be the most common exception in my experience, because they are (often inconveniently) iterable. The advantage of checking for str is that it is a very special type that is unlikely to be duck typed, while set, list, tuple, etc. are often interchangeable for snippets like the one you show here.

If you don't require a set, you can wrap a single-string links in any other container type you like: [links], (links,), etc. I would recommend avoiding set since it gets rid of ordering information, while sequence types do not.

Mad Physicist
  • 107,652
  • 25
  • 181
  • 264
1

If links can be a set or a single string element, use isinstance to check and massage your links into a set (list) before your loop. For Python 3 check for str type, for Python 2 - basestr, to be both 2 and 3 compatible I use six package

from six import string_types
if isinstance(links, string_types): 
    links = [links] 
for link in links...

Remark 1. If your indeed need set rather than list try set([link]) or {link} instead of [link].

Remark 2. You can go for a one liner with a ternary operator, yet I usually usually go for an assignment, IMHO, more readable and easy to follow when you have a special data cleaning step.

Serge
  • 3,387
  • 3
  • 16
  • 34
-1

One simple thing would be to add the following line (assuming that your set of strings is a list of strings):

  if type(links) is not list:
      links=[links]

This will make sure that your single string is a list (that consists of 1 string)

Mikhail Genkin
  • 3,247
  • 4
  • 27
  • 47
  • This is (a) not a very good way to do it, and (b) OP is looking for a set, not a list. – Mad Physicist Oct 29 '18 at 19:26
  • 1
    You are checking the type with `is` rather than `isinstance`. `is` does not allow for subclasses, for one thing. Also, in this case it would be better to check against the more restrictive type, `str`, rather than `list`, which prevents you from using any other type of sequence or iterable. – Mad Physicist Oct 29 '18 at 19:30