You can see for yourself using DMVs. In SSMS open two query tabs. Paste this into one:
select * from sys.dm_tran_active_transactions
select * from sys.dm_tran_locks
Paste your statements into the other.
Run my queries. This will show background activity on the server which can be ignored.
Run just your BEGIN TRAN A;
statement and re-run the DMV queries. You'll see there's a new transaction with name=A
but no further locks.
Now hightlight just your select statement and run it and also the DMV queries again. Nothing has changed! What happened to the S locks? Well, running under the default READ COMMITTED isolation level the locks are release as soon as the DB engine has finished processing the particular row. See section "SQL Server Locking Read Committed" here. So the S locks were taken while the query was execution but had been released by the time the DMVs were examined again. (You can see the locks and releases happening using extended events. There are queries in this answer which you can modify to see this for yourself.)
We can make the locks remain to the end of the transaction using the HOLDLOCK table hint. Change your SELECT to ..FROM Production.Products WITH(HOLDLOCK)..
and re-run the SELECT and the DMV queries. You will see shared locks on the table (and other objects).
Run just the UPDATE statement. The shared (S) locks have become exclusive (X) locks. Because these locks are protecting altered data they are always held until the end of the transaction - no additional hints are required.
Running your COMMIT will release the locks and remove the transaction.
For your original question - when was the exclusive lock taken on row with productid 3 - the answer is, when the query processor came to that particular row as it was working through the query plan generated for that update statement.