Как вывести значение ссылки в python
[Черновик] Python 3, объекты, ссылки и пространства имён
На форуме БУ нередко сталкиваюсь с игроделами, которые пытаются писать скрипты методом научного тыка (а иногда даже ненаучного) и бездумным переделыванием других скриптов. Полагая, что это происходит из-за непонимания сути питоновых объектов, я попытаюсь наглядно показать, что вообще творится в питоне во время создания и изменения всяких разных переменных и классов.
(Мне почему-то постоянно говорят, что в BGE питон какой-то особенный и «дайте мне урок на блендеровый питон» и всё такое, мне уже начинает надоедать писать: ОБЫЧНЫЙ ПИТОН И ПИТОН В БЛЕНДЕРЕ АБСОЛЮТНО ОДИНАКОВЫЕ И СОВЕРШЕННО НИЧЕМ НЕ ОТЛИЧАЮТСЯ! В BGE лишь можно импортировать модуль bge, но и для него верно абсолютно всё написанное ниже и в любых других уроках по питону.)
В Python всё является объектом. Массивы, классы, строки, числа, функции, модули, типы и даже None являются полноценными объектами, которые содержат в себе различные атрибуты и методы.
Объекты сами по себе ниоткуда не берутся. При запуске скрипта гарантировано присутствуют лишь следующие объекты:
__builtins__
__doc__
__name__
__package__
И с ними сразу можно что-то делать:
print(__name__)
И даже Blender Game Engine не добавляет никаких объектов: ни cont, ни scene, ни obj никакие ниоткуда сами не берутся: их должен доставать откуда-то сам скрипт. (А делается это импортированием модуля bge и вызовом оттуда нужных функций вроде bge.logic.getCurrentController().)
Объект __builtins__ — особый объект, он содержит в себе все стандартные типы и функции. Когда вы пишете:
import bge
то на самом деле выполняется примерно следующее:
bge = __builtins__.__import__("bge")
По сути через этот __builtins__ всё и работает, и можно, например, что-нибудь в нём сломать (например, тот же импорт).
def imp(*args, **kwargs):
print("Я СЛОМАЛ ИМПОРТ. ")
__builtins__.__import__ = imp
import bge # вывод: Я СЛОМАЛ ИМПОРТ.
print(bge) # вывод: None
При создании переменной (любой: хоть число, хоть массив, хоть модуль, хоть None — у нас всё объекты) в неё записывается ссылка на объект, а сам объект валяется где-то в оперативной памяти далеко от самой переменной со ссылкой.
arr = [1, 2, 3]

Схематично такое выглядит так:
(Сами переменные, разумеется, тоже хранятся где-то в оператинвой памяти, но во избежание загромождения рисунка я буду рисовать их (и иногда сами объекты) отдельно.)
Дальше важно понимать, что при присваивании объекта переменной с самим объектом ничего не творится: в переменную лишь записывается ссылка на него и всё. Поэтому при таком действии
b = arr

Получится следующее:
Соответственно, если мы попробуем в объекте что-нибудь поменять, обе переменные всё равно будут возвращать одно и то же.
b[1] = 8
print(arr) # вывод: [1, 8, 3]
print(b) # вывод: [1, 8, 3]


И сам объект можно рассматривать как кучку переменных, доступ к которым можно получить с помощью этой самой точки. Собственно выше я и подменил значение переменной __builtins__.__import__ на своё:
Удалить объект нельзя, можно лишь удалить все ссылки на него.
del imp
__builtins__.__import__ = None


Но, как правило, когда ссылок на объект не остаётся, просыпается сборщик мусора и всё удаляет.
Создание собственных объектов осуществляется созданием класса и экземпляров этого класса. Класс тоже является объектом.
class Test:
a = 0
b = 0

(CPython заранее создаёт объекты с некоторыми маленькими числами, чтобы побыстрее работало)
А вот дальше начинается магия. При создании экземпляра класса ОН БУДЕТ ПУСТОЙ, лишь переменная __class__ будет в нём.
obj = Test()

В случае, если мы попытаемся обратиться к переменной a нашего obj, он при её отсутствии у себя вытянет её из своего класса:
print(obj.a) # вывод: 0
Test.a = 3
print(obj.a) # вывод: 3

А вот при попытке присвоить obj.a какое-нибудь число мы уже создадим переменную в самом объекте:
obj.a = 7
print(Test.a) # вывод: 3
print(obj.a) # вывод: 7
del obj.a
print(obj.a) # вывод: опять 3
Собственно такое присваивание чаще всего и используют в функции __init__, которая вызывается каждый раз при создании экземпляра класса. Кстати, все функции, объявленные внутри класса, принимают первым аргументом объект, через который была вызвана функция.
class Num:
def __init__(self, n):
self.num = n
def show(self):
print(self.num)
obj1 = Num(3)
obj2 = Num(4)
print(obj1.num) # вывод: 3
obj1.show() # вывод: 3
print(obj2.num) # вывод: 4
obj2.show() # вывод:4
С функциями всё то же самое, что и с объектами: в самом объекте их нет, они есть лишь в классе. И их можно вызывать прямо из класса.
Num.show(obj2) # вывод: 4
И функцию в классе тоже можно подменить (но делать этого лучше не надо):
def fakenum(self):
print(self.num + 100)
Num.show = fakenum
obj1.show() # вывод: 103
Но тут уже должен был возникнуть вопрос: что вообще за self, что за аргументы, откуда они взялись, где и как хранятся? Тут пора вводить понятие пространства имён. Оно определяет, какие переменные в каком месте будут видны. Ранее до появления функций мы работали с глобальным пространством имён. Все эти __builtins__, arr, Test, Num, obj1 и obj2 хранились именно в нём. Технически это самый обычный словарь, то есть абсолютно такой же объект, как и все остальные. Его можно получить с помощью функции globals() и творить с ним что угодно.
globals()['a'] = 5
print(a) # вывод: 5
globals()['a'] = "Hello"
print(a) # вывод: Hello
Помимо этого есть локальное пространство имён, которое у каждой функции своё. Вне функций локальное пространство имён совпадает с глобальным, и это можно проверить, получив его с помощь функции locals():
print( globals() is locals() ) # вывод: True
Так как это такие же самые объекты, их (точнее, ссылки) можно точно так же присвоить любой переменной. Не забывайте, что всё есть объекты, а переменные есть ссылки на эти объекты.
g = globals()
print(g['g']) # вывод: и так далее
# это же получилась ссылка на самого себя!
print(g['g']['g']['g']['g']['g']['g']['g']['g']['g']['g']['g']['g']['g']['g'])
# вывод: и так далее
print(globals() is g['g']['g']['g']) # вывод: True

При вызове функции создаётся своё локальное пространство имён, в которое скидываются все переданные аргументы.
def test(a, b): print( locals() )
test(3, 4) # вывод:
test(«q», «w») # вывод:
Разумеется, вне функции достучаться до её локального пространства имён никак нельзя (у каждого её вызова оно своё, функция может выполняться одновременно в нескольких потоках каждый со своим локальным пространством имён, может вообще не выполняться и так далее). (А вот к тому глобальному пространству имён, в котором она работает (у каждого модуля своё глобальное пространство имён), можно достучаться через атрибут __globals__)
При обращении к переменной приоритет имеет локальное пространство имён: если переменная с требуемым названием там существует, то возвращается значение из этого самого локального пространства имён; если её там нет, то из глобального; если и там нет, то питон ругается ошибкой. При создании переменных внутри функции они создаются в локальном пространстве имён (даже если такая же переменная есть в глобальном). Чтобы использовать только глобальную переменную, можно использовать ключевое слово globals. И далее в примерах всё сказанное выше:
a = 5
def test1():
print(a)
test1() # вывод: 5
def test2(a):
print(a)
test2(3) # вывод: 3 - имеется локальная переменная a
test2(None) # вывод: None - это тоже объект, и переменная так же существует
def test3(b):
a = b
print(a)
print(a) # вывод: 5
test3(3) # вывод: 3
print(a) # вывод: по-прежнему 5, потому что функция создала локальную переменную a
def test4(b):
globals a
a = b
print(a)
print(a) # вывод: 5
test4(0) # вывод: 0
print(a) # вывод: 0 - функция заменила значение глобальной переменной
Ссылки на объекты Python
В языке Python нет переменных, вместо них используются ссылки на объ- екты. При работе со значениями интерпретатор Python сначала создает объект соответствующего типа, а затем создает ссылку на этот объект, при этом опера- тор (=) связывает ссылку со значением, что практически эквивалентно присваи- ванию:
Имена, используемые для идентификации ссылок, должны начинаться либо с латинской буквы, либо со знака подчеркивания, остальные символы мо- гут быть либо латинскими буквами, либо цифрами, либо дополнительными знаками, кроме предопределенных конструкций языка.
Для вывода на экран значений объектов можно использовать функцию print (), для ввода значений — input () . x = input(«Введите x = «) print(«Значение x равно «, x)
6.6.3 Коллекции данных Python
В языке программирования Python имеется несколько типов коллекций, способных хранить произвольное число элементов данных любых типов. Рас- смотрим два типа коллекций: кортежи и списки.
Кортежи являются неизменяемыми объектами, поэтому после создания кортеж нельзя изменить. В списках, в отличие от кортежей, можно изменять, добавлять или удалять элементы. Кортежи создаются следующим образом: («один», «два», «три», «четыре», «пять»)
Списки заключаются в квадратные скобки, элементы списка также отде- ляются друг от друга запятыми:
[1, 1, 2, 3, 5, 8, 13, 21]
[«Иванов», «Петров», «Сидоров»]
Для того чтобы получить количество элементов кортежа или списка, можно использовать функцию len(), аргументом которой является интере- сующий кортеж или список. Для кортежа или списка можно создать именован- ную ссылку и работать с ней как с объектом, обладающим своими методами, например, вставка дополнительного элемента в список осуществляется сле- дующим образом:
pupil = [«Иванов», «Петров», «Сидоров»]
Также можно удалять элементы с помощью метода remove (), вставлять элементы в заданную позицию с помощью метода insert () .
Получить доступ к отдельным элементам кортежей или списков можно с использованием оператора квадратных скобок:
num[4] = num[0] + num[1] + num[2]+ num[3]
После выполнения этого программного кода на экран будет выведено значение последнего элемента списка num — 10.
Python bs4 достать текст из ссылки?
Друзья, всем привет!
Закончил один вуз (технический) и пошел на программную инженерию. Первый год в вузе будет легким, по этому я решил выбрать python как основной язык для написания кода.
Учить решил с простых задач, и одна из них метод парса с помощью библиотек BeautifulSoup
import requests from bs4 import BeautifulSoup html = requests.get('http://site.ru') # get-запрос if html.status_code == 200: print('Connecting.. ОК') else: print('Connecting.. Error') soup = BeautifulSoup(html.text,'html.parser') link = soup.find('div', class_='titlecard') print (link)
Теперь как с полученного результата вытащить «НАВСТРЕЧУ ВСЕМ К 12.00»?
- Вопрос задан более трёх лет назад
- 2380 просмотров
Переменные-ссылки в 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 = serveraddr = "127.0.0.1" server = # хочется создать имена-сокращения для этих элементов 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 — это улучшило бы читаемость кода. Возможно ли это реализовать?
Отслеживать
9,366 4 4 золотых знака 40 40 серебряных знаков 56 56 бронзовых знаков
задан 30 апр 2015 в 1:19
1,808 2 2 золотых знака 20 20 серебряных знаков 25 25 бронзовых знаков
«Не для экономии памяти же?» — Для безопасности же. По идее питон не может упасть от ошибок сегментации или стать уязвимым от переполнения буферов. И да, ссылки по определению работают точно так же, как в C++ (не надо путать ссылки с указателями).
30 апр 2015 в 10:39
3 ответа 3
Сортировка: Сброс на вариант по умолчанию
В 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 =
После этого, если мы получаем откуда-то нашего пользователя, мы можем изменить нужные значения:
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)
