Подтвердить что ты не робот

Почему Python возвращает True при проверке, является ли пустая строка другой?

Мой ограниченный мозг не может понять, почему это происходит:

>>> print '' in 'lolsome'
True

В PHP эквивалентное сравнение возвращает false:

var_dump(strpos('', 'lolsome'));
4b9b3361

Ответ 1

Из документации:

Для Unicode и типов строк x in y является истинным тогда и только тогда, когда x является подстрокой y. Эквивалентный тест y.find(x) != -1. Обратите внимание: x и y не должны быть одного типа; следовательно, u'ab' in 'abc' вернет True. Пустые строки всегда считаются подстрокой любой другой строки, поэтому "" in "abc" вернет True.

От взгляда на ваш вызов print вы используете 2.x.

Чтобы углубиться, посмотрите на байт-код:

>>> def answer():
...   '' in 'lolsome'

>>> dis.dis(answer)
  2           0 LOAD_CONST               1 ('')
              3 LOAD_CONST               2 ('lolsome')
              6 COMPARE_OP               6 (in)
              9 POP_TOP
             10 LOAD_CONST               0 (None)
             13 RETURN_VALUE

COMPARE_OP - это то, где мы выполняем нашу булевскую операцию и смотрим на исходный код для in, где показано сравнение:

    TARGET(COMPARE_OP)
    {
        w = POP();
        v = TOP();
        if (PyInt_CheckExact(w) && PyInt_CheckExact(v)) {
            /* INLINE: cmp(int, int) */
            register long a, b;
            register int res;
            a = PyInt_AS_LONG(v);
            b = PyInt_AS_LONG(w);
            switch (oparg) {
            case PyCmp_LT: res = a <  b; break;
            case PyCmp_LE: res = a <= b; break;
            case PyCmp_EQ: res = a == b; break;
            case PyCmp_NE: res = a != b; break;
            case PyCmp_GT: res = a >  b; break;
            case PyCmp_GE: res = a >= b; break;
            case PyCmp_IS: res = v == w; break;
            case PyCmp_IS_NOT: res = v != w; break;
            default: goto slow_compare;
            }
            x = res ? Py_True : Py_False;
            Py_INCREF(x);
        }
        else {
          slow_compare:
            x = cmp_outcome(oparg, v, w);
        }
        Py_DECREF(v);
        Py_DECREF(w);
        SET_TOP(x);
        if (x == NULL) break;
        PREDICT(POP_JUMP_IF_FALSE);
        PREDICT(POP_JUMP_IF_TRUE);
        DISPATCH();
    }

и где cmp_outcome находится в одном файле, легко найти наш следующий ключ:

res = PySequence_Contains(w, v);

который находится в abstract.c:

{
    Py_ssize_t result;
    if (PyType_HasFeature(seq->ob_type, Py_TPFLAGS_HAVE_SEQUENCE_IN)) {
        PySequenceMethods *sqm = seq->ob_type->tp_as_sequence;
        if (sqm != NULL && sqm->sq_contains != NULL)
            return (*sqm->sq_contains)(seq, ob);
    }
    result = _PySequence_IterSearch(seq, ob, PY_ITERSEARCH_CONTAINS);
    return Py_SAFE_DOWNCAST(result, Py_ssize_t, int);
}

и для выхода из источника из источника, мы находим следующую функцию в документации :

objobjproc PySequenceMethods.sq_contains

Эта функция может использоваться PySequence_Contains() и имеет ту же подпись. Этот слот может быть оставлен до NULL, в этом случае PySequence_Contains() просто перемещает последовательность до тех пор, пока не найдет совпадение.

и далее в той же документации:

int PySequence_Contains(PyObject *o, PyObject *value)

Определите, содержит ли o значение. Если элемент в o равен значению, верните 1, в противном случае верните 0. При ошибке возвратите -1. Это эквивалентно выражению Python value in o.

Где '' не null, можно считать, что последовательность 'lolsome' содержит ее.

Ответ 2

Цитата из PHP strpos документации,

mixed strpos ( string $haystack , mixed $needle [, int $offset = 0 ] )

Найти числовое положение первого вхождения needle в строке haystack.

Итак, то, что вы на самом деле пытались, похоже на конструкцию Python, представленную ниже

>>> print 'lolsome' in ''
False

Итак, на самом деле вы должны написать, как показано ниже, чтобы иметь соответствующее сравнение в PHP

var_dump(strpos('lolsome', ''));

Даже тогда он выдает предупреждение и возвращает false.

Предупреждение PHP: strpos(): пустая стрелка в /home/thefourtheye/Desktop/Test.php в строке 3

bool(false)

Я углубился и нашел исходный код, соответствующий функции strpos,

    if (!Z_STRLEN_P(needle)) {
        php_error_docref(NULL, E_WARNING, "Empty needle");
        RETURN_FALSE;
    }

Они рассматривают пустую строку, которую ищут как проблемный случай. Таким образом, они выдают предупреждение и возвращают false. Помимо этого, я не мог найти ни одного документа, в котором обсуждается, почему его рассматривают как проблему.

Что касается Python, это поведение хорошо определено в разделе Сравнения,

Пустые строки всегда считаются подстрокой любой другой строки, поэтому "" in "abc" вернет True.

Ответ 3

В принципе, из математики:

Пустое множество является подмножеством каждого множества

Здесь же работает такая же логика. Вы можете рассмотреть '' пустой набор. И поэтому это подмножество каждого набора строк, так как они должны быть одного и того же типа.

>>> a = ""
>>> b = "Python"
>>> a in b
True
>>> set(a).issubset(b)
True
>>> a = set() #empty set
>>> b = set([1,2,3])
>>> a.issubset(b)
True
>>> 

Но будьте осторожны! Подмножество и членство разные вещи.

enter image description here

Ответ 4

Пустая строка - это единственная строка нулевой длины.
Пустая строка - это элемент идентификации операции конкатенации.
Пустая строка предшествует любой другой строке в лексикографическом порядке, потому что она является самой короткой из всех строк.
Пустая строка - это законная строка, на которой должны работать большинство строковых операций.
Wikipedia

 > strlen("");
=> 0
 > "a" . "" == "a";
=> true
 > "" . "a" == "a";
=> true   
 > "" < "\0";
=> true   

Сверху кажется, что PHP обрабатывает пустую строку как допустимую строку.

> strstr("lolsome", "");
strstr(): Empty needle :1

Но он не считает пустую строку полностью легитимной. Скорее всего, PHP является единственным языком, который не позволяет искать подстроку в строке как пустую строку.

Это защитный механизм? Очевидно, что программистам не нужно защищать иглу if. Если да, то почему другие языки позволяют пройти этот тест!!! Языковые дизайнеры должны отвечать

Какая строка Python состоит из?

>>> ''.count('')
1

Очевидно, что пустая строка имеет одну пустую строку.

>>> 'a'.count('')
2

Одна строка элемента имеет два пустых srings.

>>> 'ab'.count('')
3

Итак, кажется, что строка Python является конкатенацией одной строки элемента. Каждый элемент в строке зажат между двумя пустыми строками.

>>> "lolsome".split('')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: empty separator

Но здесь Python противоречит действительности пустой строки. Это ошибка?
Ruby и JavaScript проходят здесь тест.

 > "lolsome".split("")
=> ["l", "o", "l", "s", "o", "m", "e"]

Я собрал несколько примеров из Rosetta code, интересно отметить, что все они разрешают пустую строку в поиске подстроки и возвращают true.

AWK

awk 'BEGIN { print index("lolsome", "") != 0 }'

C

int main() {
    printf("%d\n", strstr("lolsome", "") != NULL);
    return 0;
}

С++

#include <iostream>
#include <string>

int main() {
    std::string s = "lolsome";
    std::cout << (s.find("") != -1) << "\n";
    return 0;
}

С#

using System;
class MainClass {
  public static void Main (string[] args) {
    string s = "lolsome";
    Console.WriteLine(s.IndexOf("", 0, s.Length) != -1);
  }
}

Clojure

(println (.indexOf "lolsome" ""))

Перейти

package main

import (
    "fmt"
    "strings"
)
func main() {
    fmt.Println(strings.Index("lolsome", "") != -1)
}

Groovy

println 'lolsome'.indexOf('')

возвращает 0, при ошибке возвращается -1

Java

class Main {
  public static void main(String[] args) {
    System.out.println("lolsome".indexOf("") != -1);
  }
}

JavaScript

"lolsome".indexOf("") != -1

Lua

s = "lolsome"
print(s:find "" ~= nil)

Perl

print index("lolsome", "") != -1;

Python

"lolsome".find("") != -1

Ruby

"lolsome".index("") != nil

Ответ 5

Предположим, у вас есть две груды похожих предметов, например, лучшие строфы вашего любимого поэта, 5 и 2 соответственно. Большой набор содержит меньший набор? Как проверить: 1) для любой строфы в меньшей куче вы можете найти ее в более крупной. 2) меньшая куча не содержит чего-то, чего нет в более крупном.

Таким образом, мы можем использовать этот псевдокод для проверки:

for object in smaller:
    if object not in bigger:
       return 'we found object from smaller absent in bigger'
    else:
       go to next object
return 'all is ok - all objects from smaller are in bigger'

Если вы не нашли такого объекта, вы подходите к концу algo и считаете, что меньше - это подмножество большего размера.

Теперь представьте меньшую кучу 0 строф. Применяя те же правила выше, мы выполняем 0 проверок, а также не находим объект меньшего размера, который больше отсутствует.

Итак, правильно и удобно считать пустую строку как подмножество любой другой строки. Даже сам. И это реализовано в python.

    >>> '' in 'adfsf'
    True
    >>> '' in ''
    True