Alex
Підписатись

Alex

Приєднався: 4 роки тому | 9 Читає   15 Читачів | 2K

Адмін сайту

  1. Це можна зробити так:

    list_3 = [ f'{i}-{j}' for i, j in zip(list_1, list_2) ]
    

    або так

    list_3 = [*map(lambda x: f'{x[0]}-{x[1]}', zip(list_1, list_2))]
    

    або так

    list_3 = [*map(lambda x: '-'.join(x), zip(list_1, list_2))]
    

    або так

    list_3 = [*map(lambda x, y: f'{x}-{y}', list_1, list_2)]
    

    або так

    list_3 = [*map('{}-{}'.format, list_1, list_2)]
    

    або так (поки що найкоротший запис)

    list_3 = [*map('-'.join, zip(list_1, list_2))]
    

  2. Присвоювання деструктуризації - це спеціальний синтаксис, який дозволяє нам «розпакувати» масиви або об'єкти в купу змінних, так як іноді вони зручніші. Деструктуризація також прекрасно працює зі складними функціями, які мають багато параметрів, значень за замовчуванням і так далі.

    // в нас є масив з двома елементами
    let arr = ["Red", "Green"]
    
    // Присвоювання деструктуризації
    // записує first=arr[0], second=arr[1]
    let [first, second] = arr;
    
    console.log(first); // Red
    console.log(second); // Green
    

  3. Для вибору необхідної версії скористайтеся командою

    sudo update-alternatives --config php
    

    І далі з запропонованого списку виберіть ту яка вам необхідна


  4. Для цього вам необхідно додати PPA репозиторій ppa:ondrej/php

    sudo apt-get update
    sudo apt -y install software-properties-common
    sudo add-apt-repository ppa:ondrej/php
    sudo apt-get update
    

    Після чого ви зможете встановити останню версію php командою

    sudo apt -y install php8
    

  5. Це можна зробити наступним чином

    use Symfony\Component\HttpFoundation\StreamedResponse;
    
    $generator = $service->getGenerator();
    
    $response = new StreamedResponse();
    $response->headers->set('Content-Type', 'text/xml');
    
    $response->setCallBack(function () use($generator) {
    	foreach($generator as $item) {
    		echo View::make('some-item', 'item' => $item]);
    	}
    });
    
    return $response;
    

  6. Розглянемо варіанти детальніше

    Варіант 1. Зберігати одну загальну сіль для всіх паролів користувачів поза БД

    hash(secret + password)
    

    На перший це виглядає безпечніше, адже якщо хтось отримає доступ до БД, то солі у ньому не буде, і напевно відновити паролі не зможе? ..

    Але якщо зловмисник отримає доступ і до БД, і до солі зі змінної середовища, то для добування паролів все одно треба буде будувати райдужну таблицю. І якщо у вас одна сіль на всіх користувачів, то це зробити набагато простіше. Інша проблема з безпекою: якщо зловмисник (або його співучасник/жертва) має доступ до системи як користувач, то він вже знає 1 пароль і його хеш, можливо зможе змінювати пароль і дивитися зміну хеша, знаходити в БД інших користувачів з таким же паролем...

    Цей варіант схожий на принцип Security through obscurity (безпека через неясність/заплутування), а цього принципу радять уникати і покладатися на сильніші криптографічні методи.

    Варіант 2. Зберігати свою для кожного користувача сіль в БД з хешамі паролів

    hash(salt + password)
    

    В цьому випадку, якщо зловмисник отримає доступ до БД, то разом з хешами паролів він отримає і солі до них. Але тепер для отримання паролів треба буде згенерувати не одну райдужну таблицю на всіх, а для кожного користувача свою! При цьому, важливо коректно реалізувати свою систему роботи з користувачами: при зміні пароля треба міняти і сіль, щоб навіть маючи стару версію БД, зловмисник вже не зміг отримати з цього вигоду.

    Цей же спосіб використовується в Django, також його радять і обгрунтовують в аналогічному питанні на enSO - раджу почитати, там обговорені різні варіанти і їх обгрунтування.

    Питання насправді спірне і залежить від реалізації вашої системи. Якщо у вас лише 1 або кілька серверів з повним або слабо захищеним доступом один до одного, то швидше за все зловмисник отримавши доступ до БД, легко отримає доступ і до ОС зі змінними оточення, і навпаки, що нівелює сенс першого варіанту. Якщо ж у вас велика і продумана інфраструктура, доступи до БД і серверів з кодом розмежовані, налаштовані жорсткі групи безпеки, всюди використовуються користувачі з обмеженнями і в БД, і в ОС, то можна отримати вигоду з першого способу. Тому є наступний варіант.

    Варіант 3. Змішати обидва варіанти

    Можна поєднати краще від двох світів, наприклад так:

    hash(hash(salt + secret) + password)
    

    Хоча є й інші варіанти, деталі можна знайти все в тому ж питанні . Але знову ж таки, користі в цьому не сильно більше, ніж у другому варіанті, особливо якщо ваша серверна інфраструктура слабо захищена.


  7. Методи map, forEach, і reduce дійсно мають багато спільного, що і коли використовувати залежить від того який саме результат за підсумком потрібно отримати.

    Уявімо що у нас є масив:

    const locations = [
      {country: "Україна", population: 2},
      {country: "Китай", population: 100},
      {country: "США", population: 20}
    ]
    

    Якщо нам потрібно просто виконати певну дію для кожного елемента масиву, то тут підійде метод forEach :

    locations.forEach(location => {
      console.log(location)
      // мы отримаємо такий вивід на консоль
      // по одному для кожнго елемента масиву
      // {country: "Україна", population: 2}
      // {country: "Китай", population: 100}
      // {country: "США", population: 20}
    })
    

    Або так, якщо нам потрібно лише певне значення

    locations.forEach(location => {
      console.log(location.country)
      // Україна
      // Китай
      // США
    })
    

    Або так, по кількості значень в масиві

    locations.forEach(() => {
      console.log("Як справи?")
      // Як справи?
      // Як справи?
      // Як справи?
    })
    

    Якщо ми хочемо трансформувати наш масив і занести результати в змінну, то тут метод map буде доречніше ніж forEach, оскільки перший за підсумком відразу поверне новий масив.

    Подивимося на різницю forEach vs map в разі коли нам потрібно "обернути" кожну власну назву країни в HTML тег:

    // через forEach
    const result = []
    locations.forEach(location => {
      result.push(`<h1>${location.country}</h1>`)
    })
    console.log(result)
    // ["<h1>Україна</h1>", "<h1>Китай</h1>", "<h1>США</h1>"]
    

    З використанням map

    const result = locations.map(location => `<h1>${location.country}</h1>`)    
    console.log(result)
    // ["<h1>Україна</h1>", "<h1>Китай</h1>", "<h1>США</h1>"]
    

    Метод reduce відрізняється від map тим що він повертає фінальне "значення" .

    Припустимо ми хочемо порахувати сумарний розмір населення:

    const result = locations.reduce((total, location) => {
      return total + location.population
    }, 0)
    
    console.log(result)
    // 122
    

    Це досить простий приклад, але "фінальним значенням" може бути не тільки число, а, наприклад, об'єкт або масив, що робить метод reduce дійсно потужним інструментом трансформацій.


  8. При написанні досить великого шматка коду (неважливо, класу, функції або чого-небудь ще) важливим прийомом є декомпозиція. Ви ділите функціональність на логічні частини, і в головній функції складаєте з них, як з цеглинок, загальну логіку.

    При цьому самі цеглинки можуть бути не пристосовані для доступу зовні. Наприклад, відкритий метод може перевіряти вхідні параметри, а допоміжним методам-цеглинам це вже не потрібно, оскільки вони викликаються лише зсередини. Якби ці методи були відкритими, в них потрібно було б реалізовувати перевірку параметрів, і при їх імплементації не можна було б розраховувати на те, що вони будуть викликані в контрольований вами момент.

    Крім того, внутрішні методи мають право псувати стан класу, якщо ви знаєте, що код який їх викликає потім цю проблему виправить. Якщо зробити ці методи відкритими, користувачі цього класу зможуть зіпсувати внутрішній стан класу без виправлення.

    Потім, внутрішні методи можуть не мати нічого спільного з тим, що повинен надавати клас, з його зовнішнім інтерфейсом. Наприклад, якщо клас представляє машину, то відкритий метод в ньому, що переводить дюйми в сантиметри, виглядав би безглуздо. А ось всередині такий переклад цілком може знадобитися.

    Ну і в кінці-кінців, публічний метод - це обіцянка для користувачів. Кожна зміна (видалення, зміна сигнатури, а іноді і додавання) відкритого методу - breaking change для клієнтів, вони повинні переглянути код, який використовує ваш клас. Таким чином, ви не повинні просто так, без особливої на то потреби міняти відкриті методи класу. А ось зміни в закритих методах зазвичай відбуваються при рефакторінгу в масовому порядку: методи спрощуються, об'єднуються, розкладаються на декілька, переносяться вгору-вниз по ієрархії, змінюється їх семантика, і все це ніяк не відбивається на користувачах вашого класу.


  9. У документації по Obj-C використовуються терміни class object і class instance. Якщо їх дослівно перекласти, то вийде якраз те, про що Вас запитували. Причому в українській мові природньо об'єкт класу прийнято вважати його экземпляром. У той час, як очевидно, що означає англійський термін class object - цей об'єкт, в якому зберігається, так би мовити, інформація про клас.

    Class Objects A class definition contains various kinds of information, much of it about instances of the class:

    The name of the class and its superclass A template describing a set of instance variables The declarations of method names and their return and argument types The method implementations This information is compiled and recorded in data structures made available to the runtime system . The compiler creates just one object, a class object , to represent the class. The class object has access to all the information about the class , which means mainly information about what instances of the class are like . It's able to produce new instances according to the plan put forward in the class definition .

    Although a class object keeps the prototype of a class instance , it's not an instance itself . It has no instance variables of its own and it can not perform methods intended for instances of the class . However, a class definition can include methods intended specifically for the class object -class methods as opposed to instance methods. A class object inherits class methods from the classes above it in the hierarchy , just as instances inherit instance methods.


  10. Основна ідея - поліпшення переносимості. Не гарантується, що на різних системах виконуваний файл буде лежати по шляху, який вказаний в shebang.

    Використання env дозволяє знизити цей ризик за рахунок запуску команди на основі даних зі змінної середовища PATH

    Більш того, якщо з яких-небудь причин замість стандартного файлу користувач хоче використовувати свій, то йому достатньо додати шлях до цього файлу в PATH без необхідності виправлення скриптів:

    ~ $ cp /bin/bash /home/soon/python                                         
    ~ $ export PATH=/home/soon:$PATH                                                 
    ~ $ env python                                                                   
    [soon@archlinux ~]$ exit
    

    В наведеному вище прикладі я скопіював bash до себе в домашню директорію (перейменувавши при цьому файл в python), додав шлях в PATH і запустив python за допомогою env, яка запустила bash, тому що знайшла його раніше.

    Ще одним прикладом є використання віртуальних оточень при розробці на Python (virtualenv). Оскільки вони також перебивають PATH, env дозволяє використовувати потрібну версію виконуваного файлу:

    ~ $ workon black-box-challenge-2016-py2     
    ~ (venv:black-box-challenge-2016-py2) $ env python
    
    Python 2.7.11 (default, Mar 31 2016, 06:18:34) 
    [GCC 5.3.0] on linux2
    Type "help", "copyright", "credits" or "license" for more information.
    >>> import sys
    >>> print sys.executable
    /home/soon/.virtualenvs/black-box-challenge-2016-py2/bin/python
    >>> 
    

  11. Різниця між PUT і POST - це питання семантики. Оскільки для операцій використовуються різні дієслова, то і сенс у них повинен бути різним.

    Уявіть, що ваш сервіс оперує поняттями блокнот (notebook) і запис (post). Один блокнот може містити безліч записів.

    Для додавання нового запису в блокнот c ідентифікатором id ви будете використовувати метод POST з URL mydomain/notebooks/id/. Ваш сервіс, орієнтуючись на метод POST, сам присвоїть потрібний ідентифікатор запису, додасть її в блокнот і поверне вам URL створеного запису (для доступу до запису по GET або для видалення по DELETE). При цьому добре б повернути клієнту URL створеної записи.

    Припустимо, запис з ідентифікатором post-id вже створено і він доступний по URL mydomain/notebooks/id/posts/post-id. Але клієнт (власник запису) виправив в ній помилку і хоче перезаписати її. Для цього він використовує метод PUT з URL mydomain/notebooks/id/posts/post-id і передає оновлений запис в тілі запиту. Ваш сервіс, орієнтуючись на метод PUT видаляє старий запис і записує новий, при цьому він доступний за тим же URL.

    Звичайно, ніхто не заважає вам завжди використовувати метод POST (наприклад HTML 4 дозволяв використовувати тільки методи GET і POST). Але все ж варто дотримуватися рекомендацій з метою однакового трактування методів усіма розробниками.

    Рекомендується використовуватися метод POST для створення підлеглого ресурсу (дочірнього по відношенню до іншого ресурсу; приклад блокнота і записи якраз дуже підходить).


  12. Оператор == порівнює посилання.

    Метод equals порівнює значення.

    Отже, якщо ви хочете порівняти рядки на рівність, слід використовувати equals.

    Однак в деяких випадках рядки гарантовано представлені одним і тим же об'єктом завдяки пулу рядків (string interning). Ці випадки явно описані в специфікації мови Java .

    Оператор == використовується для перевірки, що два рядки вказують на один і той же об'єкт.

    // Ці рядки мають одне і те саме значення
    new String("test").equals("test") // --> true 
    
    // ...але це різні об'єкти
    new String("test") == "test" // --> false 
    
    // ...ці рядки також різні об'єкти
    new String("test") == new String("test") // --> false 
    
    // ...але ці рядки вказують а один і той самий об'єкт
    // дому що компілятор додає всі літерали в пул
    "test" == "test" // --> true 
    
    // Об'єднання літералів токож відбувається на стадії компіляції
    // тому вони вказують на один об'єкт
    "test" == "te" + "st" // --> true
    
    // але виклик substring() відбувається під час виконання,
    // в результаті виходять різні об'єкти
    "test" == "!test".substring(1) // --> false
    
    // Рядки з пулу можуть буди отримані за допомогою виклику intern().
    "test" == "!test".substring(1).intern() // --> true
    

    Треба відзначити, що == помітно швидше, ніж equals (порівняння посилання замість виклику методу і посимвольного порівняння, якщо рядки різної довжини), тому, якщо ви працюєте з рядками з пулу (або системного, або свого), заміна equals на == може привести до помітного прискорення. Але це трапляється дуже рідко.

    Остерігайтеся виклику equals на null! Оператор == прекрасно порівнює рядки, якщо один або більше з них дорівнює null, але виклик методу equals на рядок, що дорівнює null, призведе до виключення.

    Для порівняння рядків, які можуть бути рівні null, ви можете скористатися наступним методом:

    public static boolean equals(String str1, String str2) {
        return str1 == null ? str2 == null : str1.equals(str2);
    }
    

    Він присутній в деяких сторонніх бібліотеках, наприклад, в Apache Commons.

    Якщо ви користуєтеся сучасними середовищами розробки, то вони попередять, якщо ви спробуєте порівняти рядки за допомогою оператора ==. Завжди звертайте увагу на подібні попередження.