Страницы

Поиск по вопросам

суббота, 23 марта 2019 г.

Переменные-ссылки в Python

Я так понял, что все переменные в Python - это ссылки, которые ссылаются на один объект, а не на участок памяти как в С/C++.
А есть ли возможность создания ссылки на один участок памяти, чтобы изменялось значение при работе с любой из ссылок?
Например (условно),
a = 10 b = &a print(a, b) # 10 10 b = 20 print(a, b) # 20 20 a = 15 print(a, b) # 15 15
Мне это нужно, чтобы сокращать запись для доступа ко внутренним значениям вложенных словарей:
username = "test" userdata = [10, 2, 30, 15] user = {username:userdata}
serveraddr = "127.0.0.1" server = {serveraddr:user}
# хочется создать имена-сокращения для этих элементов joincount = server[serveraddr][username][0] leftcount = server[serveraddr][username][1] gamescount = server[serveraddr][username][2] wincount = server[serveraddr][username][3]
# пробуем изменить их значения joincount = 20 leftcount = 9 gamescount = 10 wincount = 3
# проверяем, что значения в словаре остались прежними print(server[serveraddr][username][0], server[serveraddr][username][1], server[serveraddr][username][2], server[serveraddr][username][3]) # (10, 2, 30, 15)
# хотя значения переменных изменились print joincount, leftcount, gamescount, wincount # (20, 9, 10, 3)
В коде очень часто используются конструкции вида server[serveraddr][username][n], большая их часть приходится на места где все индексы вычислены. Поэтому хотелось бы вместо server[serveraddr][username][0] использовать joincount - это улучшило бы читаемость кода.
Возможно ли это реализовать?


Ответ

В Python есть изменяемые и неизменяемые типы. Изменяемые отличаются тем, что их содержимое можно сменить, не изменив ссылку на них. Неизменяемые объекты приходится пересоздавать, чтобы отразить изменения состояния. При этом все старые ссылки не видят это обновление, потому что указывают на старый объект.
Поясню на практике. Списки, словари, множества - это изменяемые объекты:
l1 = [1, 2, 3] l2 = l1 print(l1, l2, id(l1), id(l2)) # [1, 2, 3] [1, 2, 3] 139917408901064 139917408901064
l1[1] = 10 print(l1, l2, id(l1), id(l2)) # [1, 10, 3] [1, 10, 3] 139917408901064 139917408901064
Числа, строки, кортежи - это неизменяемые объекты:
v1 = 1024 v2 = v1 print(v1, v2, id(v1), id(v2)) # 1024 1024 ...7040 ...7040
v1 = 2048 print(v1, v2, id(v1), id(v2)) # 2048 1024 ...5312 ...7040
t1 = (1, 2, 3) t2 = t1 print(t1, t2, id(t1), id(t2)) # (1, 2, 3) (1, 2, 3) ...6232 ...6232
# t1[1] = 10 # не сработает, так как кортежи неизменяемые t1 = (1, 10, 3) print(t1, t2, id(t1), id(t2)) # (1, 10, 3) (1, 2, 3) ...7240 ...6232
В своём коде вы используете изменяемые структуры данных, такие как словари и списки (user, userdata, server) и неизменяемые строки и числа.
Таким образом, ничего не изменяя в структуре хранения информации, максимально можно сократить только доступ до списка чисел
my_userdata = server[serveraddr][username]
# изменение my_userdata влечёт изменение server[serveraddr][username], # так как это один и тот же объект
Так как числа неизменяемые, то сохранение их в переменную и дальнейшее изменение этой переменной никак не сказываются на исходном числе (т.е. переменная joincount никогда не изменится при изменении server[serveraddr][username][0], и наоборот). изменение чисел в этом списке никак не отразится на переменных, хранящих эти значения

Но если попробовать немного улучшить код, то стоит создать класс UserData, который будет хранить в себе нужные значения и позволять их изменять. Минимальный пример будет следующий:
class UserData: def __init__(self, joincount, leftcount, gamescount, wincount): self.joincount = joincount self.leftcount = leftcount self.gamescount = gamescount self.wincount = wincount
username = "test" userdata = UserData(10, 2, 30, 15) user = {username:userdata}
После этого, если мы получаем откуда-то нашего пользователя, мы можем изменить нужные значения:
user_data = server[serveraddr][username]
user_data.joincount = 20 user_data.leftcount = 9 user_data.gamescount = 10 user_data.wincount = 3
print (server[serveraddr][username].joincount, server[serveraddr][username].leftcount, server[serveraddr][username].gamescount, server[serveraddr][username].wincount)
print (user_data.joincount, user_data.leftcount, user_data.gamescount, user_data.wincount)
Использование подобной структуры увеличит читаемость вашего кода, по сравнению с использованием индексов списка.

Если есть доступ на изменение содержимого словаря, я бы рекомендовал вам вместо самописного класса использовать namedtuple из стандартного модуля collections, который позволяет создавать объект, по поведению схожий с кортежем, но предоставляющий доступ к полям, как у класса выше (но только на чтение):
UserData = collections.namedtuple('UserData', ['joincount', 'leftcount', 'gamescount', 'wincount'])
В этом случае придётся заменять старый объект UserData новым:
old_userdata = server[serveraddr][username] new_userdata = UserData(joincount=20, leftcount=9, gamescount=10, wincount=3)
server[serveraddr][username] = new_userdata print(new_userdata, old_userdata, server[serveraddr][username])
Получение поля на чтение будет выполняться так же, как и в примере выше:
print(old_userdata.joincount, old_userdata.gamescount)

Java. Создание объекта. Советы про использование String

Я недавно начал изучать Java и у меня возникло несколько вопросов.
Мне непонятно почему в Java нет простого механизма (метода) ввода\ вывода чего либо. То есть когда в Pascal - это write
ead или Python - print\input. В Java есть стандартное средство вывода - System.out.println, но ввод нужно реализовывать через импорт Scanner Подскажите это я чего-то не понимаю или реально в таком мощном языке нету таких базовых методов? Также как я понял Scanner подходит лишь для текста, а что если мне нужно вводить числа? Что использовать? Можно полностью написать как это будет выглядеть. Объясните пожалуйста, что значат аргументы в этой команде.
public static void main(String[] args) Можно объясните принцип создание экземпляра класса. Например:
String variable = new String ("Text");
Почему почему дважды вызывается класс String в начале и при создании? Я понимаю, что можно просто смирится и все, но мне не понятно зачем это действие, есть для этого объяснение или это сделано просто так?


Ответ

Java - объектно-ориентированный язык в отличие от Паскаля, и его богатая система классов ввода-вывода, базирующаяся на потоках (streams), позволяет использовать одни и те же механизмы для любых ситуаций: работа с консолью, работа с файлами, работа с сетью, работа с архивами.
Раз речь зашла про класс Scanner - обратимся к документации
Класс предоставляет кучу методов для разных ситуаций:
String next(String pattern) - вернет строку, если она соответствует шаблону; boolean nextBoolean() - вернет значение булева типа; int nextInt() - вернет целочисленное значение; float nextFloat() - вернет значение с плавающей точкой типа float
Это не все, то умеет Scanner, но теперь вы знаете, где искать.

Это не команда. Это объявление публичного (public - доступного всем) статического (static - доступного без создания экземпляра класса) метода main, который принимает массив строк (String[] args) и ничего не возвращает (void).
Согласно спецификации, метод с такой сигнатурой является точкой входа в вашу программу. То есть, при запуске в командной строке java MyClass выполнение программы начнется с этого метода в классе MyClass. В качестве массива args будут переданы аргументы из командной строки. Например, при запуске java MyClass foo bar массив args будет иметь значение ["foo", "bar"].

почему дважды вызывается Класс String в начале и при создании
String variable = new String ("Text");
Класс не "вызывается", класс нельзя "вызвать". В данном случае происходит три действия:
Объявляется переменная variable типа String Создается новый экземпляр типа String через конструктор с одним аргументом "Text" Переменной variable присваивается ссылка на созданный объект.
В случае со строками это можно было сократить до вида:
String variable = "Text";
PS. Крайне рекомендую начать изучение Java, вооружившись одним из учебников для начинающих, там есть ответы на подобные вопросы: Книги по Java и другая литература

PPS. В следующий раз, пожалуйста, соблюдайте правила и задавайте по одному вопросу за раз.

Подсчитать максимальное количество отрицательных элементов идущих подряд

Нужно подсчитать максимальное количество отрицательных элементов идущих подряд. Что-то не могу понять как сделать правильно. На данный момент считает просто все отрицательные элементы.
#include #include using namespace std;
void main() { setlocale(LC_ALL, "ukr"); srand(time(NULL)); const int n = 15; bool change = true; int arr[n], m = 0;
for (int i = 0; i < n; i++) { arr[i] = rand() % 10 - 5; cout << arr[i] << " "; } cout << endl; int maxcount = 0; for (int i = 0; i < n; i++) { int count = 0; if (arr[i] < 0) { for (int j = i; j < n; j++) { if (arr[j] < 0) { count++; if (count > maxcount) { maxcount = count; } cout << count << " "; } } } } cout << endl << maxcount << endl; }


Ответ

for (int j = i; j < n; j++) { if (arr[j] < 0) { count++; if (count > maxcount) { maxcount = count; }
}else{ count = 0; //cбрасывать значение count кто будет? } }
пробный запуск:
-3 -5 -4 3 4 -5 1 0 -3 -1 -1 -2 -5 -5 0
6

Подвинуть border относительно картинки


Хочу сделать так. Понятно, что чтобы выдвинуть на передний план, нужно добавить z-index. Но как сделать подобный border относительно изображения?


Ответ

Решение через border и использование div
.image { position: relative; width: 150px; height: 150px; background-image: url("https://graph.facebook.com/625308780945294/picture?type=large"); } .image:before { content: ""; position: absolute; border: 2px solid #c745bc; left: 20%; top: 20%; width: 100%; height: 100%; z-index: -1; }


Разница между методом и функцией

Есть ли разница между методом и функцией? Или это просто два разных названия одного и того же?


Ответ

И да, и нет.
Если разбираться детально, то и функция и метод - это некоторый фрагмент кода, который можно вызвать по имени и, опционально, передать ему какие-либо параметры. С этой точки зрения различий никаких нет.
С другой стороны, функция обычно включает в себя все необходимое для своей работы, а взаимодействие с внешним миром осуществляет через входные и выходные параметры, а также возвращаемое значение (использование глобальных переменных скорее исключение, а чаще просто грубая ошибка). Кроме того, функция является самостоятельной единицей и может быть вызвана в любом месте программы без ограничений (ну почти, необходимость подключения библиотек и т.д. оставим в стороне).
Метод же для своей работы может использовать поля объекта и/или класса, в котором определен, напрямую, без необходимости передавать их во входных параметрах. Это похоже на использование глобальных переменных в функциях, но в отличие от глобальных переменных, метод может получать прямой доступ только к членам класса. Метод привязан к классу, если он статический, или объекту и может быть вызван только через этот класс или объект.
Из-за этих, весьма существенных, отличий метод и получил отдельный термин, в некотором смысле сокращение от "Метод (способ) взаимодействия с классом или объектом".
В целом, называть метод функцией не является ошибкой (обратное неверно), главное понимать концептуальные отличия этих терминов.
PS: Это касается любых языков программирования, поддерживающих концепцию объектно-ориентированного программирования.

Создание переменной ссылочного типа при каждой итерации цикла. Это затратно?

Есть вот такой код:
... Match match; foreach (var value in egeQuestionValues) { match = Regex.Match(value, $@"(?<={number}\()\d+(?=.*%\))"); if (!match.Success) throw new ArgumentException($"The record {value} is incorrect", nameof(egeQuestionValues));
egeQuestionIntValues.Add(int.Parse(match.Value)); }
return egeQuestionIntValues.Average();
Я создал переменную match специально вне цикла foreach, чтобы в рантайме не создать ее при каждой итерации. Делал, думаю что это затратно. Но ReSharper мне советует отрефакторить этот код таким образом:
... foreach (var value in egeQuestionValues) { var match = Regex.Match(value, $@"(?<={number}\()\d+(?=.*%\))"); if (!match.Success) throw new ArgumentException($"The record {value} is incorrect", nameof(egeQuestionValues));
egeQuestionIntValues.Add(int.Parse(match.Value)); }
return egeQuestionIntValues.Average();
т.е. сейчас при каждой итерации переменная match будет создаваться каждый раз. Разве это не затратно? ReSharper советует поступать так не только с типом Match и с другими типами так было.


Ответ

Разницы особой нету.
Все переменные ссылочного типа без инициализации автоматически инициализируются null-значением.
Что сама по себе переменная ссылочного типа? Это переменная, которая ссылается на какой-то адрес.
Смена ссылки - это быстрая операция, так как не создается никаких дополнительных объектов, как это происходит со значимым типом.
Скорее всего, компилятор приведет код к первому случаю и IL-код будет идентичным.
На мой взгляд овчинка выделки не стоит и это не узкое место, ведь вы программируете на высокоуровневом языке программирования, где на первое место нужно ставить понятность кода, а уж потом если где-то что-то тормозит, то оптимизировать.
Второй вариант более приятен, так как переменная находится прямо в месте ее использования и можно сразу понять, что за циклом она нигде использоваться не будет.

Метод в C#, меняющий местами значения переменных

Я новичок в программировании на C#, попался вопрос про обмен значениями переменных без использования третьей. С одним из решений все понятно:
int a = 2; int b = 4; a = a + b; b = a - b; a = a - b;
У меня другой вопрос, можно ли это как-то засунуть в метод? Написал так:
class Program { public static void Main(string[] args) {
Console.WriteLine("Введите значения переменных a и b"); Console.Write("a = ");
int x = int.Parse(Console.ReadLine());
Console.Write("b = ");
int y = int.Parse(Console.ReadLine());
SwapNumbers(x, y); Console.Write($"Переменная a = {x}, переменная b = {y} "); Console.ReadLine();
}
public static void SwapNumbers(int a, int b) { a = a + b; b = a - b; a = a - b; } }
Но переменные x и y не меняются местами, по крайне мере в методе Main. Можно сделать так:
public static void SwapNumbers(int a, int b) { a = a + b; b = a - b; a = a - b; Console.Write($"Переменная a = {a}, переменная b = {b} "); }
Но это выглядит как-то коряво и неуместно, потому что я хочу, чтобы метод только менял местами введенные с клавиатуры значения, а где и когда их вывести я хочу решать сам.


Ответ

Всё дело в том, что ты передаешь копии переменных в метод Swap. Чтобы иметь возможность передавать ссылку есть ключевое слово ref
Т.е. метод будет выглядеть так:
public static void Main(string[] args) { int a = 7, b = 5; Console.WriteLine("a -> {0}; b -> {1}", a, b); Swap(ref a, ref b); Console.WriteLine("a -> {0}; b -> {1}", a, b); }
public static void Swap(ref int a, ref int b) { a = a + b; b = a - b; a = a - b; }
Есть также параметр out. Разница между ref и out тут: ref и out