Автор Тема: Правилно ли е това поведение на Python set  (Прочетена 3847 пъти)

4096bits

  • Напреднали
  • *****
  • Публикации: 6152
    • Профил
В Python set тип данни са неподредени, неповтарящи се променливи и при print в интерпретатора например или дори в скрипт без форматиране би трябвало да са затворени в {}. Понеже са неподрен набор променливи при set.pop() би трябвало да връща произволна променлива от всички. Да, ама не. Поне така си го представям, а и така пише за set и set.pop()
Обаче в интерпретатора при print всичките променливи са затворени между ([...]). При set.pop() не се връща произволна променлива от всички, ами първата, по реда както е нацвъкано всичкото set.
В IPython при print ми изкарва цялата променлива от тип set в {..} скоби, но пак при var.pop() ми връща "поредни" стойности от целия куп. Няма индекс на всичкото променливи, няма подреждане, но сякаш има. Някой да ми обясни моля, защото аз вече се обърках здраво. Помня, че преди време като пробвах как се държи set тип данни, можеше да се вади произволна стойност от всичките, но сега нещо не става. Също не мога да превърна list в set. Не дава грешка, при проверка ми казва, че е торбата със стойности е тип set(), но се държи отново по този странен начин. И отново го принтва в ([...]). Ако се опитам да набутам стойностите една по една с цикъл for в празна променлива тип set, отново на финала ми ги принтва при  проверка на положението в такива двойни скоби, а не в {...}. Отново са тип set(), отново са "неподредени" стойности вътре, но все пак при pop() се връщат в реда, в който са вкарани, а не в произволен ред. При опит да ги достъпя по индекс разбира се ми реве за грешка, както и трябва да бъде.
Активен

As they say in Mexico, "Dasvidaniya!" Down there, that's two vidaniyas.

gat3way

  • Напреднали
  • *****
  • Публикации: 6050
  • Relentless troll
    • Профил
    • WWW
Re: Правилно ли е това поведение на Python set
« Отговор #1 -: Feb 21, 2016, 01:53 »
Аз пък даже не знаех че съществували set-ове в python.

Обаче не ме учудва толкова, то питона си е омазан език за мазане, еле пък и документацията да очакваш да е винаги коректна.

Хубавото е че абсолютно всички типове всъщност са класове и абсолютно всичките им методи са виртуални, малка гавра с ООП концепцията.

Та ако искаш да си направиш тип set, който да се държи "коректно" няма да е сложно да унаследиш set и да му направиш свой си pop() метод дето работи коректно, нещо от сорта на:

Код:
class myset(set):
   def pop(self):
      # Дали пък няма някакъв len атрибут дето да връща броя елементи в set-а, не знам, така че да го правим по тъпия начин
      len = 0
      for el in self:
           len += 1
      toremove = random.randrange(0,len)
      i=0
      result = None
      for el in self.copy():
          if i == toremove:
              result = el
              self.remove(el)
          i+=1   
     return result

...

orig_set = {"a","b","c"}
proper_set = myset(orig_set)
proper_set.pop()


Или нещо от сорта. В смисъл надращих го набързо (единствено проверих дали set-овете са iterable) и изобщо не съм го тествал, та може и да има грешка някъде, отделно не е оптимално, но въпросът е да се илюстрира идеята в крайна сметка.

И да, пачваме нещо дето трябва да работи както е документирано, но както става ясно не го прави.
« Последна редакция: Feb 21, 2016, 01:56 от gat3way »
Активен

"Knowledge is power" - France is Bacon

4096bits

  • Напреднали
  • *****
  • Публикации: 6152
    • Профил
Re: Правилно ли е това поведение на Python set
« Отговор #2 -: Feb 21, 2016, 15:51 »
Това дето си написал, не вади ли определен елемент от сета?! Само дето го избира случайно.
Не съм го пробвал, но сетовете понеже са неподредени множества, не може да се поиска елемент от тях по индекс, понеже нямат индекс. Пък бил определен и случайно.
Иначе това, за което говорех си работеше нормално. По дяволите!
В момента седя на една дебианска система и в терминала python пуска python3. Та там

x = range(1, 101)

дава стойност на x, която е равна на range(1, 101). Без кавички. И x е от тип range  ???
Сега, xrange е заменена от  range в python3, която е iterable, но не трябва ли да се изпълни функцията и да се даде стойност на х изявяваща се в лист от едно до сто?!
Нещо не ми се струва наред.
Вчера като писах, седях на едно Минт и бях пускал ъпгрейд преди няколко дена, та си помислих, че сега на дебиана би трябвало да е така, както се очаква. Не е ъпгрейдван. Почва да ме боли главата.
Активен

As they say in Mexico, "Dasvidaniya!" Down there, that's two vidaniyas.

korea60

  • Напреднали
  • *****
  • Публикации: 189
    • Профил
Re: Правилно ли е това поведение на Python set
« Отговор #3 -: Feb 21, 2016, 17:15 »
Хм, това с xrange е интересно, качих си python3 за да го видя и наитина е малко объркано, но резултата се изпринтва коректно с list(x), както е във вашият пример.
В python2 type(x) = "list"
В пътхон3 е както казвате renge.

За сета наистина си работи така, но и там поне за мен има още нещо нередно.

x = set(1,4,3,2) и x = set(10,40,30,20), първото е сортирано второто не, не виждам защо не се сортира второто при положение че липсват стойности от вида: 9,10,8,45?

Активен

4096bits

  • Напреднали
  • *****
  • Публикации: 6152
    • Профил
Re: Правилно ли е това поведение на Python set
« Отговор #4 -: Feb 21, 2016, 20:45 »
Значи декларацията на променлива от тип set е така:

var = set()

Така var e от тип set. Тип set е като list с разликата, че стойностите вътре не са подредени, може да добавят или махат нови елементи, но понеже са неподредени, не може да индексират. Прибавянето на елементи, не става в края или началото на var. Var.remove('a') например ще премахне 'a', ако е във var, var.pop() ще премахне елемент от var, но няма да е от края на var, понеже var не може да се индексира и няма определен последен елемент. Var.pop() би трябвало по тази причина да измъкне и да върне произволен елемен от var, както пише и в описанието на функцията.
Иначе декларирането на променлива тип set се прави със {...}, а не с (...).
x = {'a', 'b', 'c', 'd'}
или така, ако x не е инициализирана:
x = set()

Както и да е. Print x би трябвало да извади това:
{'a', 'b', 'c', 'd'}

Но не.
Вади го в тези скоби - ([...)] сякаш е tuple с list вътре, а не set.
Кофтито е, че var.pop() ми бърка нещата, тъй като трябва да извади случайна стойност от var.
Помня, че преди около три седмици var.pop() си вадеше произволни стойности от var и всичко беше, както се очаква.
Дали да не им пиша на онез.

Сега гледам, че имало и модул Sets, та печатането на променлива от тоя тип си се прави в точно тези скоби ([...]). Почвам много да се обърквам. Това не си ли е вграден тип променлива? ???
« Последна редакция: Feb 21, 2016, 20:51 от 4096bits »
Активен

As they say in Mexico, "Dasvidaniya!" Down there, that's two vidaniyas.

gat3way

  • Напреднали
  • *****
  • Публикации: 6050
  • Relentless troll
    • Профил
    • WWW
Re: Правилно ли е това поведение на Python set
« Отговор #5 -: Feb 22, 2016, 01:46 »
Ами "произволен" и "случайно избран определен елемент" не се припокриват на 100%, но ако трябва да спорим доколко са взаимозаменяеми, ще отекат нещата.

range() в питон 3 е xrange() в питон 2 и е обяснимо защо не връща list-а. Това е интересен въпрос обаче, защото и на мен ми беше едно от нещата в този език (то не е специфично питонска парадигма де), за които ми трябваше време да ги осмисля като хората. Но са относително фундаментални там.

Краткия отговор е че xrange() е генератор. В смисъл пак си е обикновена функция, но в реализацията й никъде нe се return-ва нищо,  съответно няма как да се eval-не и да ти върне резултат. Всъщност, кода вътре в тази функция изобщо не се изпълнява когато я извикаш, веднага ти се връща генератор.

Това е понеже вътре във функцията, вместо return, има yield. Смисълът от това е когато тръгнеш да я итерираш (генераторът е iterable). Това обаче става по малко странен начин - когато итерираш първият елемент, кода на функцията се изпълнява и връща резултата от първия срещнат yield. Всяка следваща итерация "продължава" изпълнението на функцията от следващият ред код след yield-а, докато не срещне следващият yield и съответно не върне следващия резултат. Ефективно това означава, че резултатите ти се генерират on-the-fly, вместо да се връща един списък, който да итерираш, това има понякога радикални последствия (при големи списъци най-вече). Еквивалентът на нещо подобно в C би бил реализиран по някакъв касапски и склонен на грешки начин с нещо от сорта на setjmp()/longjmp() - нормалните хора гледат да си нямат вземане-даване с това.


Що се отнася до това какво ти вади print за типа set - както казах в питон всичко е обект, всички типове са класове, това което пропуснах да спомена е че по "конвенция" има няколко "служебни" метода, които никой не ти пречи да overload-неш, такъв е __str__(). Последният връща "printable" версия на обекта и е нещото, което print вади. Та за моя пример с "коректния set", просто трябва да си дефинираш и __str__() метода и да връщаш какъвто си искаш низ, въпросния ще го изпише print. Съответно да връщаш нещото опаковано в къдрави скоби вместо квадратни, няма проблем.

« Последна редакция: Feb 22, 2016, 01:48 от gat3way »
Активен

"Knowledge is power" - France is Bacon

4096bits

  • Напреднали
  • *****
  • Публикации: 6152
    • Профил
Re: Правилно ли е това поведение на Python set
« Отговор #6 -: Feb 22, 2016, 03:46 »
Да, xrange връща генератор.  Не трябва ли да сметне дясната страна на = и да присъедини резултата на лявата?! Поне така е при нормалана инициализация на променлива.

Може би, ако затворя xrange в map? Нещо така map(xrange(1, 101)).
Мога да пробвам, вместо да пиша тук, но просто ми е бръмнала главата вече и дори не ми се отваря интепретатор. От други задачки.

Цялата приказка е, че преди две-три седмици, както споменах, пробвах как се държи set за сравнение с останалите типове и тогава си стана и pop() и нормалното принт на променливата в каквито трябва скоби. Сигурно нещо супер елементарно бъркам, ама са да ме гръмнеш, не мож се сета.
« Последна редакция: Feb 22, 2016, 03:50 от 4096bits »
Активен

As they say in Mexico, "Dasvidaniya!" Down there, that's two vidaniyas.

gat3way

  • Напреднали
  • *****
  • Публикации: 6050
  • Relentless troll
    • Профил
    • WWW
Re: Правилно ли е това поведение на Python set
« Отговор #7 -: Feb 23, 2016, 01:22 »
Не, той генератора не може да се "смята", може само да се итерира. В този случай очевидно идеята е била да присвоиш генератора и да можеш впоследствие да итерираш това което е от лявата страна. То и според мен така ми изглежда далеч по-логично поведение де.

Що се отнася до map, щях да напиша че няма как да стане защото това просто създава списък от резултатите на функция викната върху елементите на нещо което да е iterable...но пък на второ четене ми хрумна нещо и пробвах и очевидно може:

Цитат
>>> map(int,xrange(1,5))
[1, 2, 3, 4]

Активен

"Knowledge is power" - France is Bacon

4096bits

  • Напреднали
  • *****
  • Публикации: 6152
    • Профил
Re: Правилно ли е това поведение на Python set
« Отговор #8 -: Feb 23, 2016, 13:52 »
По погрешка си инсталирах нещо на направено върху Федора, но едва ли ще има разлика.
Значи, ето какво ми говори терминала:

>>> x = set()
>>> type(x)
<type 'set'>
>>> x = range(1,10)
>>> x
[1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> type(x)
<type 'list'>
>>> set(x)
set([1, 2, 3, 4, 5, 6, 7, 8, 9])
>>> x
[1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> x[2]
3
>>>

Веднага се вижда това променяне на типа от set на list. Точно, както става с числовите променливи. Разликата между 2 и 2.0. А все забравям за тази особеност на Python. От С помня, че декларираш ли една променлива по един начин, си остава така. Ако не ѝ кажеш изрично, да стане нещо друго.

Иначе при Python3.x ето какво се случва при тази декларация:
>>> x = set(range(1,10))
>>> x
{1, 2, 3, 4, 5, 6, 7, 8, 9}
>>> x.pop()
1
>>> x.pop()
2
>>> x.pop()
3
>>> x.pop()
4
>>> type(x)
<class 'set'>
>>>

Отново pop() вади поредни елементи сякаш x е list.
Python2:
Python 2.7.10 (default, Jul  5 2015, 14:15:43)
[GCC 5.1.1 20150618 (Red Hat 5.1.1-4)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> x = set(range(1,10))
>>> x
set([1, 2, 3, 4, 5, 6, 7, 8, 9])
>>> x.pop()
1
>>> x.pop()
2
>>> x.pop()
3
>>> x
set([4, 5, 6, 7, 8, 9])
>>> type(x)
<type 'set'>
>>>

Направо мисля да им пиша на .... онез. Които ще да са. Това абсолютно противоречи с __doc__ дето пише за set.pop(). А и в документацията.
Почвам да се чудя, как го направих оня път. Помня, че просто стана. Съвсем елементарно. Попълних сета автоматично, после извадих няколко произволни елемента с pop() и се разардвах, че съм научил нещо ново. Сега не мога да го повторя.
Активен

As they say in Mexico, "Dasvidaniya!" Down there, that's two vidaniyas.

gat3way

  • Напреднали
  • *****
  • Публикации: 6050
  • Relentless troll
    • Профил
    • WWW
Re: Правилно ли е това поведение на Python set
« Отговор #9 -: Feb 24, 2016, 11:01 »
Цитат
>>> set(x)
set([1, 2, 3, 4, 5, 6, 7, 8, 9])
>>> x
[1, 2, 3, 4, 5, 6, 7, 8, 9]

А, за това е виновен __repr__(), не __str__()

Както и да е, те и да го оправят да следва 1:1 документацията, ти като си напишеш нещото, няма да имаш гаранция на какъв питонски интерпретатор ще върви, та ако силно разчиташ на правилното поведение би било добре да си направиш собствен клас set, който се държи правилно.
Активен

"Knowledge is power" - France is Bacon

Odido

  • Напреднали
  • *****
  • Публикации: 627
  • Distribution: Arch Linux
  • Window Manager: Gnome
    • Профил
Re: Правилно ли е това поведение на Python set
« Отговор #10 -: Feb 25, 2016, 10:07 »
Доколкото съм запознат ,pop() се използва за манипулация на листове,няма как да манипулираш сетове поради  изброените от теб по-горе причини.
 Смокото вероятно  конвертира сета в лист при използване на методи ,като append ,pop,extend и прочие... 
 Всъщност могат да се индексират,но няма гаранция за поредността:

for idx, value in enumerate(x):
    print(idx, ' - ', value)

« Последна редакция: Feb 25, 2016, 10:14 от Odido »
Активен

"Congratulations, you broke the Internet
Look at what you did! Are you happy now?"

4096bits

  • Напреднали
  • *****
  • Публикации: 6152
    • Профил
Re: Правилно ли е това поведение на Python set
« Отговор #11 -: Feb 25, 2016, 11:55 »
Това не е точно индексиране
Иначе към клас set си има точно дефиниран метод pop()
pop(...)
 |      Remove and return an arbitrary set element.
 |      Raises KeyError if the set is empty.

Точно поради причините, които съм изброил, както казваш, няма append() метод. Защото не може да се прибави нещо на края на сета, тъй като няма дефиниран край. Има само метод add()
« Последна редакция: Feb 25, 2016, 11:57 от 4096bits »
Активен

As they say in Mexico, "Dasvidaniya!" Down there, that's two vidaniyas.

Подобни теми
Заглавие Започната от Отговора Прегледи Последна публикация
Python IDE
Настройка на програми
gamehack 1 3976 Последна публикация May 23, 2003, 12:03
от rat
Български счетоводен софтуер на Python
Предложения за български проект
cvludmiloff 3 4927 Последна публикация Jun 05, 2004, 17:38
от vladou
Python
Общ форум
fantom 6 4198 Последна публикация Nov 07, 2005, 18:51
от betso
Python
Общ форум
glam 0 2168 Последна публикация Jan 11, 2007, 13:00
от glam
Искам да се зарежда python 3.7 вместо python 2.7
Web development
3p0 36 17851 Последна публикация Dec 27, 2019, 00:49
от 4096bits